libsql2

An alternative to turso_libsql with a native Rust extension.

Features

  • Native Rust extension — no FFI overhead
  • Full libSQL support — local files, in-memory, remote (Turso Cloud), and Embedded Replicas

Installation

Add to your Gemfile:

gem "libsql2"

Then run:

bundle install

Usage

Opening a Database

require "libsql2"

# Local file
db = Libsql::Database.open("/path/to/file.db")

# In-memory
db = Libsql::Database.open(":memory:")

# Remote (Turso Cloud)
db = Libsql::Database.open("libsql://xxx.turso.io", auth_token: "xxx")

# Embedded Replica
db = Libsql::Database.open("libsql://xxx.turso.io",
  auth_token: "xxx",
  local_path: "/path/to/replica.db",
  sync_interval: 60,
  read_your_writes: true
)

# Block form (auto-close)
Libsql::Database.open(":memory:") do |db|
  # ...
end

Querying

db.connect do |conn|
  conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
  conn.execute("INSERT INTO users (name, age) VALUES (?, ?)", ["Alice", 30])

  rows = conn.query("SELECT * FROM users WHERE age > ?", [20])
  rows.columns  #=> ["id", "name", "age"]
  rows.types    #=> ["INTEGER", "TEXT", "INTEGER"]
  rows.each { |row| puts row["name"] }
end

Execute (no result set)

conn.execute("INSERT INTO users (name) VALUES (?)", ["Alice"])
conn.changes            #=> 1
conn.last_insert_rowid  #=> 42

Batch Execution

conn.execute_batch(<<~SQL)
  CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);
  CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER, title TEXT);
SQL

Prepared Statements

stmt = conn.prepare("SELECT * FROM users WHERE name = ?")
stmt.bind(["Alice"])
rows = stmt.query
stmt.close

Transactions

conn.transaction do |tx|
  tx.execute("UPDATE accounts SET balance = balance - 100 WHERE id = ?", [1])
  tx.execute("UPDATE accounts SET balance = balance + 100 WHERE id = ?", [2])
  # Commits on normal exit, rolls back on exception
end

# With behavior
conn.transaction(:immediate) do |tx|
  tx.execute("INSERT INTO orders (item) VALUES (?)", ["widget"])
end

# Nested transactions (SAVEPOINT)
conn.transaction do |tx|
  tx.execute("INSERT INTO users (name) VALUES (?)", ["Alice"])
  tx.transaction do |tx2|
    tx2.execute("INSERT INTO users (name) VALUES (?)", ["Bob"])
  end
end

Embedded Replica Sync

db.sync  # Manual sync

Fork Safety (Puma / Unicorn)

# puma.rb
on_worker_boot do
  $db.discard! if $db
  $db = Libsql::Database.open(...)
end

Type Mapping

Query Results (libSQL → Ruby)

libSQL Type Ruby Type Notes
INTEGER Integer Signed 64-bit
REAL Float Double precision
TEXT String UTF-8 encoded
BLOB Libsql::Blob String subclass with ASCII-8BIT encoding
NULL nil

Bind Parameters (Ruby → libSQL)

Ruby Type libSQL Type Notes
Integer INTEGER
Float REAL
true / false INTEGER (1 / 0) SQLite convention — no native Boolean type
String (UTF-8 etc.) TEXT
String (ASCII-8BIT) BLOB Ruby convention for binary data
Libsql::Blob BLOB String subclass, checked before String
nil NULL

Development

Requirements: Ruby >= 3.4, Rust >= 1.74, and a C compiler.

bundle install       # Install Ruby dependencies
rake compile         # Compile the Rust extension
rake spec            # Run tests
rake lint            # Run all linters (RuboCop + Clippy)
rake fmt             # Auto-format all code

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/speria-jp/libsql-ruby2.

License

The gem is available as open source under the terms of the MIT License.