From a9b5fc7f6336e762da96b7716cdabc72dcfb5903 Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 11:30:31 -0700 Subject: [PATCH 01/10] Add tests for vector operations and date/time functions in Go adapter --- bindings/go/limbo_test.go | 325 +++++++++++++++++++++++++++++++++++++- 1 file changed, 321 insertions(+), 4 deletions(-) diff --git a/bindings/go/limbo_test.go b/bindings/go/limbo_test.go index 9527faa5f..dd0676292 100644 --- a/bindings/go/limbo_test.go +++ b/bindings/go/limbo_test.go @@ -4,13 +4,16 @@ import ( "database/sql" "fmt" "log" + "math" "testing" _ "github.com/tursodatabase/limbo" ) -var conn *sql.DB -var connErr error +var ( + conn *sql.DB + connErr error +) func TestMain(m *testing.M) { conn, connErr = sql.Open("sqlite3", ":memory:") @@ -59,7 +62,7 @@ func TestQuery(t *testing.T) { t.Errorf("Expected column %d to be %s, got %s", i, expectedCols[i], col) } } - var i = 1 + i := 1 for rows.Next() { var a int var b string @@ -78,7 +81,6 @@ func TestQuery(t *testing.T) { if err = rows.Err(); err != nil { t.Fatalf("Row iteration error: %v", err) } - } func TestFunctions(t *testing.T) { @@ -280,6 +282,321 @@ func TestDriverRowsErrorMessages(t *testing.T) { t.Log("Rows error behavior test passed") } +func TestVectorOperations(t *testing.T) { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening connection: %v", err) + } + defer db.Close() + + // Test creating table with vector columns + _, err = db.Exec(`CREATE TABLE vector_test (id INTEGER PRIMARY KEY, embedding F32_BLOB(64))`) + if err != nil { + t.Fatalf("Error creating vector table: %v", err) + } + + // Test vector insertion + _, err = db.Exec(`INSERT INTO vector_test VALUES (1, vector('[0.1, 0.2, 0.3, 0.4, 0.5]'))`) + if err != nil { + t.Fatalf("Error inserting vector: %v", err) + } + + // Test vector similarity calculation + var similarity float64 + err = db.QueryRow(`SELECT vector_distance_cos(embedding, vector('[0.2, 0.3, 0.4, 0.5, 0.6]')) FROM vector_test WHERE id = 1`).Scan(&similarity) + if err != nil { + t.Fatalf("Error calculating vector similarity: %v", err) + } + if similarity <= 0 || similarity > 1 { + t.Fatalf("Expected similarity between 0 and 1, got %f", similarity) + } + + // Test vector extraction + var extracted string + err = db.QueryRow(`SELECT vector_extract(embedding) FROM vector_test WHERE id = 1`).Scan(&extracted) + if err != nil { + t.Fatalf("Error extracting vector: %v", err) + } + fmt.Printf("Extracted vector: %s\n", extracted) +} + +func TestTransactions(t *testing.T) { + t.Skip("Skipping transaction tests - transactions not yet implemented in the limbo driver") + + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening connection: %v", err) + } + defer db.Close() + + // Create test table + _, err = db.Exec(`CREATE TABLE tx_test (id INTEGER PRIMARY KEY, value TEXT)`) + if err != nil { + t.Fatalf("Error creating table: %v", err) + } + + // Test successful transaction + tx, err := db.Begin() + if err != nil { + t.Fatalf("Error beginning transaction: %v", err) + } + + _, err = tx.Exec(`INSERT INTO tx_test VALUES (1, 'before commit')`) + if err != nil { + t.Fatalf("Error executing in transaction: %v", err) + } + + err = tx.Commit() + if err != nil { + t.Fatalf("Error committing transaction: %v", err) + } + + // Verify commit worked + var value string + err = db.QueryRow(`SELECT value FROM tx_test WHERE id = 1`).Scan(&value) + if err != nil { + t.Fatalf("Error querying after commit: %v", err) + } + if value != "before commit" { + t.Fatalf("Expected 'before commit', got '%s'", value) + } + + // Test rollback + tx, err = db.Begin() + if err != nil { + t.Fatalf("Error beginning transaction: %v", err) + } + + _, err = tx.Exec(`INSERT INTO tx_test VALUES (2, 'should rollback')`) + if err != nil { + t.Fatalf("Error executing in transaction: %v", err) + } + + err = tx.Rollback() + if err != nil { + t.Fatalf("Error rolling back transaction: %v", err) + } + + // Verify rollback worked + err = db.QueryRow(`SELECT value FROM tx_test WHERE id = 2`).Scan(&value) + if err == nil { + t.Fatalf("Expected error after rollback, got value: %s", value) + } +} + +func TestSQLFeatures(t *testing.T) { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening connection: %v", err) + } + defer db.Close() + + // Create test tables + _, err = db.Exec(` + CREATE TABLE customers ( + id INTEGER PRIMARY KEY, + name TEXT, + age INTEGER + )`) + if err != nil { + t.Fatalf("Error creating customers table: %v", err) + } + + _, err = db.Exec(` + CREATE TABLE orders ( + id INTEGER PRIMARY KEY, + customer_id INTEGER, + amount REAL, + date TEXT + )`) + if err != nil { + t.Fatalf("Error creating orders table: %v", err) + } + + // Insert test data + _, err = db.Exec(` + INSERT INTO customers VALUES + (1, 'Alice', 30), + (2, 'Bob', 25), + (3, 'Charlie', 40)`) + if err != nil { + t.Fatalf("Error inserting customers: %v", err) + } + + _, err = db.Exec(` + INSERT INTO orders VALUES + (1, 1, 100.50, '2024-01-01'), + (2, 1, 200.75, '2024-02-01'), + (3, 2, 50.25, '2024-01-15'), + (4, 3, 300.00, '2024-02-10')`) + if err != nil { + t.Fatalf("Error inserting orders: %v", err) + } + + // Test JOIN + rows, err := db.Query(` + SELECT c.name, o.amount + FROM customers c + INNER JOIN orders o ON c.id = o.customer_id + ORDER BY o.amount DESC`) + if err != nil { + t.Fatalf("Error executing JOIN: %v", err) + } + defer rows.Close() + + // Check JOIN results + expectedResults := []struct { + name string + amount float64 + }{ + {"Charlie", 300.00}, + {"Alice", 200.75}, + {"Alice", 100.50}, + {"Bob", 50.25}, + } + + i := 0 + for rows.Next() { + var name string + var amount float64 + if err := rows.Scan(&name, &amount); err != nil { + t.Fatalf("Error scanning JOIN result: %v", err) + } + if i >= len(expectedResults) { + t.Fatalf("Too many rows returned from JOIN") + } + if name != expectedResults[i].name || amount != expectedResults[i].amount { + t.Fatalf("Row %d: expected (%s, %.2f), got (%s, %.2f)", + i, expectedResults[i].name, expectedResults[i].amount, name, amount) + } + i++ + } + + // Test GROUP BY with aggregation + var count int + var total float64 + err = db.QueryRow(` + SELECT COUNT(*), SUM(amount) + FROM orders + WHERE customer_id = 1 + GROUP BY customer_id`).Scan(&count, &total) + if err != nil { + t.Fatalf("Error executing GROUP BY: %v", err) + } + if count != 2 || total != 301.25 { + t.Fatalf("GROUP BY gave wrong results: count=%d, total=%.2f", count, total) + } +} + +func TestDateTimeFunctions(t *testing.T) { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening connection: %v", err) + } + defer db.Close() + + // Test date() + var dateStr string + err = db.QueryRow(`SELECT date('now')`).Scan(&dateStr) + if err != nil { + t.Fatalf("Error with date() function: %v", err) + } + fmt.Printf("Current date: %s\n", dateStr) + + // Test date arithmetic + err = db.QueryRow(`SELECT date('2024-01-01', '+1 month')`).Scan(&dateStr) + if err != nil { + t.Fatalf("Error with date arithmetic: %v", err) + } + if dateStr != "2024-02-01" { + t.Fatalf("Expected '2024-02-01', got '%s'", dateStr) + } + + // Test strftime + var formatted string + err = db.QueryRow(`SELECT strftime('%Y-%m-%d', '2024-01-01')`).Scan(&formatted) + if err != nil { + t.Fatalf("Error with strftime function: %v", err) + } + if formatted != "2024-01-01" { + t.Fatalf("Expected '2024-01-01', got '%s'", formatted) + } +} + +func TestMathFunctions(t *testing.T) { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening connection: %v", err) + } + defer db.Close() + + // Test basic math functions + var result float64 + err = db.QueryRow(`SELECT abs(-15.5)`).Scan(&result) + if err != nil { + t.Fatalf("Error with abs function: %v", err) + } + if result != 15.5 { + t.Fatalf("abs(-15.5) should be 15.5, got %f", result) + } + + // Test trigonometric functions + err = db.QueryRow(`SELECT round(sin(radians(30)), 4)`).Scan(&result) + if err != nil { + t.Fatalf("Error with sin function: %v", err) + } + if math.Abs(result-0.5) > 0.0001 { + t.Fatalf("sin(30 degrees) should be about 0.5, got %f", result) + } + + // Test power functions + err = db.QueryRow(`SELECT pow(2, 3)`).Scan(&result) + if err != nil { + t.Fatalf("Error with pow function: %v", err) + } + if result != 8 { + t.Fatalf("2^3 should be 8, got %f", result) + } +} + +func TestJSONFunctions(t *testing.T) { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Error opening connection: %v", err) + } + defer db.Close() + + // Test json function + var valid int + err = db.QueryRow(`SELECT json_valid('{"name":"John","age":30}')`).Scan(&valid) + if err != nil { + t.Fatalf("Error with json_valid function: %v", err) + } + if valid != 1 { + t.Fatalf("Expected valid JSON to return 1, got %d", valid) + } + + // Test json_extract + var name string + err = db.QueryRow(`SELECT json_extract('{"name":"John","age":30}', '$.name')`).Scan(&name) + if err != nil { + t.Fatalf("Error with json_extract function: %v", err) + } + if name != "John" { + t.Fatalf("Expected 'John', got '%s'", name) + } + + // Test JSON shorthand + var age int + err = db.QueryRow(`SELECT '{"name":"John","age":30}' -> '$.age'`).Scan(&age) + if err != nil { + t.Fatalf("Error with JSON shorthand: %v", err) + } + if age != 30 { + t.Fatalf("Expected 30, got %d", age) + } +} + func slicesAreEq(a, b []byte) bool { if len(a) != len(b) { fmt.Printf("LENGTHS NOT EQUAL: %d != %d\n", len(a), len(b)) From 992324f31898e2929955d1878e1182d466d033eb Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 12:41:47 -0700 Subject: [PATCH 02/10] Add .gitignore for generated library files --- bindings/go/.gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 bindings/go/.gitignore diff --git a/bindings/go/.gitignore b/bindings/go/.gitignore new file mode 100644 index 000000000..d245c38b1 --- /dev/null +++ b/bindings/go/.gitignore @@ -0,0 +1,7 @@ +# Ignore generated libraries directory +/libs/ + +# Ignore any extracted libraries in development +*.dll +*.so +*.dylib From 322c2859e67b3b6ba55e2c1f7fb034b6c0cc93f3 Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 12:42:38 -0700 Subject: [PATCH 03/10] platform-specific build script that generates and organizes library for embedding into Go binaries --- bindings/go/build_lib.sh | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100755 bindings/go/build_lib.sh diff --git a/bindings/go/build_lib.sh b/bindings/go/build_lib.sh new file mode 100755 index 000000000..fb5cd4717 --- /dev/null +++ b/bindings/go/build_lib.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# bindings/go/build_lib.sh + +set -e + +echo "Building Limbo Go library for current platform..." + +# Determine platform-specific details +case "$(uname -s)" in + Darwin*) + OUTPUT_NAME="lib_limbo_go.dylib" + PLATFORM="darwin_$(uname -m)" + ;; + Linux*) + OUTPUT_NAME="lib_limbo_go.so" + PLATFORM="linux_$(uname -m)" + ;; + MINGW*|MSYS*|CYGWIN*) + OUTPUT_NAME="lib_limbo_go.dll" + if [ "$(uname -m)" == "x86_64" ]; then + PLATFORM="windows_amd64" + else + PLATFORM="windows_386" + fi + ;; + *) + echo "Unsupported platform: $(uname -s)" + exit 1 + ;; +esac + +# Create output directory +OUTPUT_DIR="libs/${PLATFORM}" +mkdir -p "$OUTPUT_DIR" + +# Build the library +cargo build --release --package limbo-go + +# Copy to the appropriate directory +echo "Copying $OUTPUT_NAME to $OUTPUT_DIR/" +cp "../../target/release/$OUTPUT_NAME" "$OUTPUT_DIR/" + +echo "Library built successfully for $PLATFORM" From 2476d2c6c23f65229950a08f9baa2ee80bf2177d Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 12:43:36 -0700 Subject: [PATCH 04/10] embeds and extracts platform-specific libraries at runtime using Go's embed package --- bindings/go/embedded.go | 133 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 bindings/go/embedded.go diff --git a/bindings/go/embedded.go b/bindings/go/embedded.go new file mode 100644 index 000000000..9f04f2d79 --- /dev/null +++ b/bindings/go/embedded.go @@ -0,0 +1,133 @@ +// Go bindings for the Limbo database. +// +// This file implements library embedding and extraction at runtime, a pattern +// also used in several other Go projects that need to distribute native binaries: +// +// - github.com/kluctl/go-embed-python: Embeds a full Python distribution in Go +// binaries, extracting to temporary directories at runtime. The approach used here +// was directly inspired by its embed_util implementation. +// +// - github.com/kluctl/go-jinja2: Uses the same pattern to embed Jinja2 and related +// Python libraries, allowing Go applications to use Jinja2 templates without +// external dependencies. +// +// This approach has several advantages: +// - Allows distribution of a single, self-contained binary +// - Eliminates the need for users to set LD_LIBRARY_PATH or other environment variables +// - Works cross-platform with the same codebase +// - Preserves backward compatibility with existing methods +// - Extracts libraries only once per execution via sync.Once +// +// The embedded library is extracted to a user-specific temporary directory and +// loaded dynamically. If extraction fails, the code falls back to the traditional +// method of searching system paths. +package limbo + +import ( + "embed" + "fmt" + "io" + "os" + "path/filepath" + "runtime" + "sync" +) + +//go:embed libs/* +var embeddedLibs embed.FS + +var ( + extractOnce sync.Once + extractedPath string + extractErr error +) + +// extractEmbeddedLibrary extracts the library for the current platform +// to a temporary directory and returns the path to the extracted library +func extractEmbeddedLibrary() (string, error) { + extractOnce.Do(func() { + // Determine platform-specific details + var libName string + var platformDir string + + switch runtime.GOOS { + case "darwin": + libName = "lib_limbo_go.dylib" + case "linux": + libName = "lib_limbo_go.so" + case "windows": + libName = "lib_limbo_go.dll" + default: + extractErr = fmt.Errorf("unsupported operating system: %s", runtime.GOOS) + return + } + + // Determine architecture suffix + var archSuffix string + switch runtime.GOARCH { + case "amd64": + archSuffix = "amd64" + case "arm64": + archSuffix = "arm64" + case "386": + archSuffix = "386" + default: + extractErr = fmt.Errorf("unsupported architecture: %s", runtime.GOARCH) + return + } + + // Create platform directory string + platformDir = fmt.Sprintf("%s_%s", runtime.GOOS, archSuffix) + + // Create a unique temporary directory for the current user + tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("limbo-go-%d", os.Getuid())) + if err := os.MkdirAll(tempDir, 0755); err != nil { + extractErr = fmt.Errorf("failed to create temp directory: %w", err) + return + } + + // Path to the library within the embedded filesystem + libPath := filepath.Join("libs", platformDir, libName) + + // Where the library will be extracted + extractedPath = filepath.Join(tempDir, libName) + + // Check if library already exists and is valid + if stat, err := os.Stat(extractedPath); err == nil && stat.Size() > 0 { + // Library already exists, nothing to do + return + } + + // Open the embedded library + embeddedLib, err := embeddedLibs.Open(libPath) + if err != nil { + extractErr = fmt.Errorf("failed to open embedded library %s: %w", libPath, err) + return + } + defer embeddedLib.Close() + + // Create the output file + outFile, err := os.Create(extractedPath) + if err != nil { + extractErr = fmt.Errorf("failed to create output file: %w", err) + return + } + defer outFile.Close() + + // Copy the library to the temporary directory + if _, err := io.Copy(outFile, embeddedLib); err != nil { + extractErr = fmt.Errorf("failed to extract library: %w", err) + return + } + + // On Unix systems, make the library executable + if runtime.GOOS != "windows" { + if err := os.Chmod(extractedPath, 0755); err != nil { + extractErr = fmt.Errorf("failed to make library executable: %w", err) + return + } + } + }) + + return extractedPath, extractErr +} From 1e0b4676dc7af88b271d96950d0a921ee7888bff Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 12:44:24 -0700 Subject: [PATCH 05/10] Update library loading mechanism to first attempt using the embedded library before falling back to traditional LD_LIBRARY_PATH lookup --- bindings/go/limbo_unix.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/bindings/go/limbo_unix.go b/bindings/go/limbo_unix.go index 8ab911416..1dd51f42e 100644 --- a/bindings/go/limbo_unix.go +++ b/bindings/go/limbo_unix.go @@ -13,6 +13,21 @@ import ( ) func loadLibrary() (uintptr, error) { + // Try to extract embedded library first + libPath, err := extractEmbeddedLibrary() + if err == nil { + // Successfully extracted embedded library, try to load it + slib, dlerr := purego.Dlopen(libPath, purego.RTLD_NOW|purego.RTLD_GLOBAL) + if dlerr == nil { + return slib, nil + } + // If loading failed, log the error and fall back to system paths + fmt.Printf("Warning: Failed to load embedded library: %v\n", dlerr) + } else { + fmt.Printf("Warning: Failed to extract embedded library: %v\n", err) + } + + // Fall back to original behavior for compatibility var libraryName string switch runtime.GOOS { case "darwin": @@ -23,7 +38,7 @@ func loadLibrary() (uintptr, error) { return 0, fmt.Errorf("GOOS=%s is not supported", runtime.GOOS) } - libPath := os.Getenv("LD_LIBRARY_PATH") + libPath = os.Getenv("LD_LIBRARY_PATH") paths := strings.Split(libPath, ":") cwd, err := os.Getwd() if err != nil { From 1a3c3866b82d9243e86a73a9822c8afc9f836fc2 Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 12:44:46 -0700 Subject: [PATCH 06/10] Update Windows library loading to prioritize the embedded library while maintaining compatibility with PATH-based lookup --- bindings/go/limbo_windows.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/bindings/go/limbo_windows.go b/bindings/go/limbo_windows.go index d56381176..2fddfd9a4 100644 --- a/bindings/go/limbo_windows.go +++ b/bindings/go/limbo_windows.go @@ -12,17 +12,33 @@ import ( ) func loadLibrary() (uintptr, error) { - libName := fmt.Sprintf("%s.dll", libName) + // Try to extract embedded library first + libPath, err := extractEmbeddedLibrary() + if err == nil { + // Successfully extracted embedded library, try to load it + slib, dlerr := windows.LoadLibrary(libPath) + if dlerr == nil { + return uintptr(slib), nil + } + // If loading failed, log the error and fall back to system paths + fmt.Printf("Warning: Failed to load embedded library: %v\n", dlerr) + } else { + fmt.Printf("Warning: Failed to extract embedded library: %v\n", err) + } + + // Fall back to original behavior + libraryName := fmt.Sprintf("%s.dll", libName) + pathEnv := os.Getenv("PATH") paths := strings.Split(pathEnv, ";") - cwd, err := os.Getwd() if err != nil { return 0, err } paths = append(paths, cwd) + for _, path := range paths { - dllPath := filepath.Join(path, libName) + dllPath := filepath.Join(path, libraryName) if _, err := os.Stat(dllPath); err == nil { slib, loadErr := windows.LoadLibrary(dllPath) if loadErr != nil { @@ -32,5 +48,5 @@ func loadLibrary() (uintptr, error) { } } - return 0, fmt.Errorf("library %s not found in PATH or CWD", libName) + return 0, fmt.Errorf("library %s not found in PATH or CWD", libraryName) } From bcb2f9f307f15c11e9e95301f865e92ab212c80d Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 12:45:30 -0700 Subject: [PATCH 07/10] add documentation for new embedded library feature, including usage instructions and implementation notes --- bindings/go/README.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/bindings/go/README.md b/bindings/go/README.md index ab50140bb..b831f0596 100644 --- a/bindings/go/README.md +++ b/bindings/go/README.md @@ -1,14 +1,30 @@ # Limbo driver for Go's `database/sql` library - **NOTE:** this is currently __heavily__ W.I.P and is not yet in a usable state. This driver uses the awesome [purego](https://github.com/ebitengine/purego) library to call C (in this case Rust with C ABI) functions from Go without the use of `CGO`. +## Embedded Library Support + +This driver now includes an embedded library feature that allows you to distribute a single binary without requiring users to set environment variables. The library for your platform is automatically embedded, extracted at runtime, and loaded dynamically. + +To build with embedded library support: +``` +# From the bindings/go directory +./build_lib.sh +``` + +If the embedded library cannot be found or extracted, the driver will fall back to the traditional method of finding the library in the system paths. ## To use: (_UNSTABLE_ testing or development purposes only) -### Linux | MacOS +### Option 1: Using the embedded library (recommended) + +Build the driver with the embedded library as described above, then simply import and use. No environment variables needed! + +### Option 2: Manual library setup + +#### Linux | MacOS _All commands listed are relative to the bindings/go directory in the limbo repository_ @@ -21,7 +37,7 @@ export LD_LIBRARY_PATH="/path/to/limbo/target/debug:$LD_LIBRARY_PATH" ``` -## Windows +#### Windows ``` cargo build --package limbo-go @@ -69,3 +85,7 @@ func main() { } } ``` + +## Implementation Notes + +The embedded library feature was inspired by projects like [go-embed-python](https://github.com/kluctl/go-embed-python), which uses a similar approach for embedding and distributing native libraries with Go applications. From aed36c5b309f7d99fd5b92bda8a60bbc0ebb24df Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 13:02:52 -0700 Subject: [PATCH 08/10] change to address CI failure. libs dir must exist with at least one file during build time. Add libs/.gitkeep as placeholder. Update .gitignore to exclude built libs but keep placeholder. This change is for CI purposes only --- bindings/go/.gitignore | 9 +++------ bindings/go/libs/.gitkeep | 0 2 files changed, 3 insertions(+), 6 deletions(-) create mode 100644 bindings/go/libs/.gitkeep diff --git a/bindings/go/.gitignore b/bindings/go/.gitignore index d245c38b1..6dd54c50a 100644 --- a/bindings/go/.gitignore +++ b/bindings/go/.gitignore @@ -1,7 +1,4 @@ # Ignore generated libraries directory -/libs/ - -# Ignore any extracted libraries in development -*.dll -*.so -*.dylib +/libs/* +# But keep the directory structure +!/libs/.gitkeep diff --git a/bindings/go/libs/.gitkeep b/bindings/go/libs/.gitkeep new file mode 100644 index 000000000..e69de29bb From 69965b4eeea8a2d896bcaa5844c7a3ba37ceb792 Mon Sep 17 00:00:00 2001 From: jnesss Date: Fri, 2 May 2025 14:44:49 -0700 Subject: [PATCH 09/10] remove TestTransactions that was being skipped. added back in second PR --- bindings/go/limbo_test.go | 64 --------------------------------------- 1 file changed, 64 deletions(-) diff --git a/bindings/go/limbo_test.go b/bindings/go/limbo_test.go index dd0676292..2758c94fd 100644 --- a/bindings/go/limbo_test.go +++ b/bindings/go/limbo_test.go @@ -320,70 +320,6 @@ func TestVectorOperations(t *testing.T) { fmt.Printf("Extracted vector: %s\n", extracted) } -func TestTransactions(t *testing.T) { - t.Skip("Skipping transaction tests - transactions not yet implemented in the limbo driver") - - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - t.Fatalf("Error opening connection: %v", err) - } - defer db.Close() - - // Create test table - _, err = db.Exec(`CREATE TABLE tx_test (id INTEGER PRIMARY KEY, value TEXT)`) - if err != nil { - t.Fatalf("Error creating table: %v", err) - } - - // Test successful transaction - tx, err := db.Begin() - if err != nil { - t.Fatalf("Error beginning transaction: %v", err) - } - - _, err = tx.Exec(`INSERT INTO tx_test VALUES (1, 'before commit')`) - if err != nil { - t.Fatalf("Error executing in transaction: %v", err) - } - - err = tx.Commit() - if err != nil { - t.Fatalf("Error committing transaction: %v", err) - } - - // Verify commit worked - var value string - err = db.QueryRow(`SELECT value FROM tx_test WHERE id = 1`).Scan(&value) - if err != nil { - t.Fatalf("Error querying after commit: %v", err) - } - if value != "before commit" { - t.Fatalf("Expected 'before commit', got '%s'", value) - } - - // Test rollback - tx, err = db.Begin() - if err != nil { - t.Fatalf("Error beginning transaction: %v", err) - } - - _, err = tx.Exec(`INSERT INTO tx_test VALUES (2, 'should rollback')`) - if err != nil { - t.Fatalf("Error executing in transaction: %v", err) - } - - err = tx.Rollback() - if err != nil { - t.Fatalf("Error rolling back transaction: %v", err) - } - - // Verify rollback worked - err = db.QueryRow(`SELECT value FROM tx_test WHERE id = 2`).Scan(&value) - if err == nil { - t.Fatalf("Expected error after rollback, got value: %s", value) - } -} - func TestSQLFeatures(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { From 091169af3811729e2c64a3f3913e47353084a6fa Mon Sep 17 00:00:00 2001 From: jnesss Date: Sat, 3 May 2025 10:23:27 -0700 Subject: [PATCH 10/10] Go uses amd64 to refer to the 64-bit x86 architecture, while Linux systems report x86_64. This change maps the architecture names to ensure the built libraries are placed in the correct directories for Go's embedded loading system (e.g., libs/linux_amd64) --- bindings/go/build_lib.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/bindings/go/build_lib.sh b/bindings/go/build_lib.sh index fb5cd4717..b89baae14 100755 --- a/bindings/go/build_lib.sh +++ b/bindings/go/build_lib.sh @@ -9,11 +9,21 @@ echo "Building Limbo Go library for current platform..." case "$(uname -s)" in Darwin*) OUTPUT_NAME="lib_limbo_go.dylib" - PLATFORM="darwin_$(uname -m)" + # Map x86_64 to amd64 for Go compatibility + ARCH=$(uname -m) + if [ "$ARCH" == "x86_64" ]; then + ARCH="amd64" + fi + PLATFORM="darwin_${ARCH}" ;; Linux*) OUTPUT_NAME="lib_limbo_go.so" - PLATFORM="linux_$(uname -m)" + # Map x86_64 to amd64 for Go compatibility + ARCH=$(uname -m) + if [ "$ARCH" == "x86_64" ]; then + ARCH="amd64" + fi + PLATFORM="linux_${ARCH}" ;; MINGW*|MSYS*|CYGWIN*) OUTPUT_NAME="lib_limbo_go.dll" @@ -34,10 +44,10 @@ OUTPUT_DIR="libs/${PLATFORM}" mkdir -p "$OUTPUT_DIR" # Build the library -cargo build --release --package limbo-go +cargo build --package limbo-go # Copy to the appropriate directory echo "Copying $OUTPUT_NAME to $OUTPUT_DIR/" -cp "../../target/release/$OUTPUT_NAME" "$OUTPUT_DIR/" +cp "../../target/debug/$OUTPUT_NAME" "$OUTPUT_DIR/" echo "Library built successfully for $PLATFORM"