Skip to content
Open
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
116 changes: 116 additions & 0 deletions bindings/lua/sqlite_vec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
-- sqlite_vec.lua Lua 5.1 compatible version with JSON fallback
local sqlite3 = require("lsqlite3")

local M = {}

-- Function to load extension
function M.load(db)
local possible_paths = {
"../../sqlite-vec.so", -- Linux
"../../sqlite-vec.dll", -- Windows
"../../sqlite-vec.dylib", -- macOS
"./sqlite-vec.so",
"./sqlite-vec.dll",
"./sqlite-vec.dylib",
"../sqlite-vec.so",
"../sqlite-vec.dll",
"../sqlite-vec.dylib",
"sqlite-vec",
}

local entry_point = "sqlite3_vec_init"

if db.enable_load_extension then
db:enable_load_extension(true)
for _, path in ipairs(possible_paths) do
local ok, result = pcall(function()
return db:load_extension(path, entry_point)
end)
if ok then
db:enable_load_extension(false)
return result
end
end
db:enable_load_extension(false)
error("Failed to load extension from all paths")
else
for _, path in ipairs(possible_paths) do
local ok, result = pcall(function()
return db:load_extension(path, entry_point)
end)
if ok then
return result
else
local ok2, result2 = pcall(function()
return db:load_extension(path)
end)
if ok2 then
return result2
end
end
end
error("Failed to load extension from all paths")
end
end

-- Lua 5.1 compatible float to binary conversion function
local function float_to_bytes(f)
if f == 0 then
return string.char(0, 0, 0, 0)
end

local sign = 0
if f < 0 then
sign = 1
f = -f
end

local mantissa, exponent = math.frexp(f)
exponent = exponent - 1

if exponent < -126 then
mantissa = mantissa * 2^(exponent + 126)
exponent = -127
else
mantissa = (mantissa - 0.5) * 2
end

exponent = exponent + 127
mantissa = math.floor(mantissa * 2^23 + 0.5)

local bytes = {}
bytes[1] = mantissa % 256; mantissa = math.floor(mantissa / 256)
bytes[2] = mantissa % 256; mantissa = math.floor(mantissa / 256)
bytes[3] = mantissa % 256 + (exponent % 2) * 128; exponent = math.floor(exponent / 2)
bytes[4] = exponent % 128 + sign * 128

return string.char(bytes[1], bytes[2], bytes[3], bytes[4])
end

-- Helper function: serialize float vector to binary format (Lua 5.1 compatible)
function M.serialize_f32(vector)
local buffer = {}

if string.pack then
for _, v in ipairs(vector) do
table.insert(buffer, string.pack("f", v))
end
else
for _, v in ipairs(vector) do
table.insert(buffer, float_to_bytes(v))
end
end

return table.concat(buffer)
end

-- New: JSON format vector serialization (more reliable fallback)
function M.serialize_json(vector)
local values = {}
for _, v in ipairs(vector) do
table.insert(values, tostring(v))
end
return "[" .. table.concat(values, ",") .. "]"
end

return M
7 changes: 7 additions & 0 deletions examples/simple-lua/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Lua specific
*.luac

# SQLite databases (if any test files are created)
*.db
*.sqlite
*.sqlite3
62 changes: 62 additions & 0 deletions examples/simple-lua/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# SQLite-Vec Simple Lua Example

This example demonstrates how to use sqlite-vec with Lua and the lsqlite3 binding.

## Prerequisites

1. **Lua** (5.1 or later) - The example is compatible with Lua 5.1+
2. **lsqlite3** - Lua SQLite3 binding
3. **sqlite-vec extension** - Compiled for your platform

## Installation

### Install lsqlite3

Using LuaRocks:
```bash
luarocks install lsqlite3
```

Or on Ubuntu/Debian:
```bash
apt-get install lua-sql-sqlite3
```

### Build sqlite-vec

From the sqlite-vec root directory:
```bash
make
```

This will create the appropriate extension file (.so, .dll, or .dylib) for your platform.

### Setup sqlite_vec.lua

You have two options:

1. **Copy the binding file** (recommended):
```bash
cp ../../bindings/lua/sqlite_vec.lua ./
```

2. **Modify the require path** in `demo.lua` to point to the bindings directory.

## Running the Example

```bash
lua demo.lua
```

Expected output:
```
=== SQLite-Vec Simple Lua Example ===
sqlite_version=3.x.x, vec_version=v0.x.x
Inserting vector data...
Executing KNN query...
Results:
rowid=3 distance=0.000000
rowid=2 distance=0.200000
rowid=1 distance=0.400000
✓ Demo completed successfully
```
139 changes: 139 additions & 0 deletions examples/simple-lua/demo.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env lua

-- Simple Lua example demonstrating sqlite-vec usage
-- This example shows how to create vector tables, insert data, and perform KNN queries

local sqlite3 = require("lsqlite3")

-- You can either:
-- 1. Copy sqlite_vec.lua from ../../bindings/lua/sqlite_vec.lua to this directory
-- 2. Or modify the path below to point to the bindings directory
local sqlite_vec = require("sqlite_vec")

function main()
print("=== SQLite-Vec Simple Lua Example ===")

-- Create in-memory database
local db = sqlite3.open_memory()
if not db then
error("Failed to create database")
end

-- Load sqlite-vec extension
local load_success, load_error = pcall(function()
sqlite_vec.load(db)
end)

if not load_success then
error("Failed to load sqlite-vec extension: " .. tostring(load_error))
end

-- Check versions - handle the case where vec_version() might not be available
local sqlite_version = nil
local vec_version = nil

for row in db:nrows("SELECT sqlite_version()") do
sqlite_version = row.sqlite_version
break
end

-- Try to get vec_version, but don't fail if it's not available
local success, _ = pcall(function()
for row in db:nrows("SELECT vec_version()") do
vec_version = row.vec_version
break
end
end)

if sqlite_version then
if vec_version then
print(string.format("sqlite_version=%s, vec_version=%s", sqlite_version, vec_version))
else
print(string.format("sqlite_version=%s, vec_version=unknown", sqlite_version))
end
end

-- Verify extension is loaded by checking for vec0 module
local vec0_available = false
for row in db:nrows("SELECT name FROM pragma_module_list() WHERE name='vec0'") do
vec0_available = true
break
end

if vec0_available then
print("✓ sqlite-vec extension loaded successfully")
else
error("sqlite-vec extension loaded but vec0 module not found")
end

-- Test data - same as other examples for consistency
local items = {
{1, {0.1, 0.1, 0.1, 0.1}},
{2, {0.2, 0.2, 0.2, 0.2}},
{3, {0.3, 0.3, 0.3, 0.3}},
{4, {0.4, 0.4, 0.4, 0.4}},
{5, {0.5, 0.5, 0.5, 0.5}},
}
local query = {0.3, 0.3, 0.3, 0.3}

-- Create virtual table
local create_result = db:exec("CREATE VIRTUAL TABLE vec_items USING vec0(embedding float[4])")
if create_result ~= sqlite3.OK then
error("Failed to create virtual table: " .. db:errmsg())
end

-- Insert data using JSON format (more compatible)
print("Inserting vector data...")
db:exec("BEGIN")

for _, item in ipairs(items) do
local rowid = math.floor(item[1])
local vector_json = sqlite_vec.serialize_json(item[2])

local sql = string.format("INSERT INTO vec_items(rowid, embedding) VALUES (%d, '%s')",
rowid, vector_json)
local result = db:exec(sql)
if result ~= sqlite3.OK then
error("Failed to insert item: " .. db:errmsg())
end
end

db:exec("COMMIT")

-- Verify data was inserted
local count = 0
for row in db:nrows("SELECT COUNT(*) as count FROM vec_items") do
count = row.count
break
end
print(string.format("✓ Inserted %d vector records", count))

-- Perform KNN query using JSON format
print("Executing KNN query...")
local query_json = sqlite_vec.serialize_json(query)

local sql = string.format([[
SELECT
rowid,
distance
FROM vec_items
WHERE embedding MATCH '%s'
ORDER BY distance
LIMIT 3
]], query_json)

print("Results:")
for row in db:nrows(sql) do
print(string.format("rowid=%d distance=%f", row.rowid, row.distance))
end

db:close()
print("✓ Demo completed successfully")
end

-- Run the demo with error handling
local success, error_msg = pcall(main)
if not success then
print("Error: " .. tostring(error_msg))
os.exit(1)
end
30 changes: 30 additions & 0 deletions examples/simple-lua/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

# Simple run script for the Lua example

echo "=== SQLite-Vec Lua Example Runner ==="

# Check if Lua is available
if ! command -v lua &> /dev/null; then
echo "Error: Lua is not installed or not in PATH"
exit 1
fi

# Check if lsqlite3 is available
lua -e "require('lsqlite3')" 2>/dev/null
if [ $? -ne 0 ]; then
echo "Error: lsqlite3 module is not installed"
echo "Install it with: luarocks install lsqlite3"
Comment on lines +16 to +17

Choose a reason for hiding this comment

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

Maybe it would be better to print errors to stderr:

Suggested change
echo "Error: lsqlite3 module is not installed"
echo "Install it with: luarocks install lsqlite3"
echo "Error: lsqlite3 module is not installed" >&2
echo "Help: luarocks install lsqlite3" >&2

exit 1
fi

# Check if sqlite-vec extension exists
if [ ! -f "../../sqlite-vec.so" ] && [ ! -f "../../sqlite-vec.dll" ] && [ ! -f "../../sqlite-vec.dylib" ]; then
echo "Error: sqlite-vec extension not found"
echo "Build it with: cd ../.. && make"
exit 1
fi

# Run the demo
echo "Running demo..."
lua demo.lua