Skip to content
Merged
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
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ The driver bundles Apple Silicon macOS and glibc Linux x86_64/aarch64 libraries.

## Connection modes

Turso Cloud's `turso://` URLs are also accepted, both as `database:`/`url:`
options and directly through `Sequel.connect('turso://...', auth_token: ...)`.
They are normalized to `https://` for the pinned native driver before opening
a connection. The same credential/query/fragment validation applies. A
`turso://` database URL is never treated as a local file path.

```ruby
# Memory: a single pooled connection.
db = Sequel.connect(adapter: :libsql)
Expand Down Expand Up @@ -127,6 +133,6 @@ bundle exec rake remote_local # real native HTTP client, local Hrana server

`tmp/` and `private_docs/` are Git-ignored and excluded from gem packaging. `private_docs/CONTINUATION.md` is the local continuation checkpoint to read after agent compaction.

For an explicit test against a **disposable** hosted database, set `LIBSQL_TEST_URL` and `LIBSQL_TEST_AUTH_TOKEN` in your environment, then run `bundle exec rake remote`. It creates randomly named test tables and drops those tables afterward. Hosted authentication, TLS, and embedded replication have not been validated in this workspace.
For an explicit test against a **disposable** hosted database, set `LIBSQL_TEST_URL` and `LIBSQL_TEST_AUTH_TOKEN` in your environment, then run `bundle exec rake remote`. It creates randomly named test tables and drops those tables afterward. Hosted authentication and HTTPS remote operations have been validated with a `turso://` URL; embedded replication remains unverified. See [compatibility and verification](docs/compatibility.md) for the tested scope.

See [architecture](docs/architecture.md), [compatibility](docs/compatibility.md), and [the local example](examples/local.rb).
3 changes: 2 additions & 1 deletion docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ As of 2026-10-31, Apple Silicon macOS, Ruby 4.0.5, `turso_libsql` 0.2.0, bundled
- Sequel upstream at `75b425dd1c1f34908426dd70371d14e4afb9c933`: SQLite adapter plus integration run passes (901 tests, six upstream skips). The test sources are unmodified. Some additional groups are never defined for an external adapter: seven SQLite type-conversion cases gated on `adapter_scheme == :sqlite` now have local equivalents. See [the coverage review](testing-review.md).
- Real native HTTP execution against the official Hrana v3 Python fixture covers prepared Unicode/blob values, affected counts, inserted IDs, constraints, transactions/savepoints, reconnect, schema metadata, empty results, NULL values, models using prepared statements, and INSERT/UPDATE/DELETE RETURNING.
- The fixture wrapper only normalizes padded/unpadded base64 between Python and Rust. SQL and stream handling stay upstream. This is a protocol integration test, not a hosted Turso test.
- Hosted Turso remote mode was verified with a `turso://` URL normalized to HTTPS and a real database token, using Sequel 5.109.0. The warning-enabled `test/remote.rb` suite passed all five tests (24 assertions), covering CRUD, prepared bindings, Unicode/blobs, constraints, transactions/savepoints/rollback, reconnect persistence, schema metadata, models, and returning writes. Only randomly named test tables were created and dropped; credentials were not logged.

Exact counts and commands are recorded in the local ignored continuation notes and test logs.

## Not verified

Hosted Turso credentials/TLS, live embedded replication, sync scheduling/read-your-writes behavior against a primary, encryption, Linux native execution, and Ruby 3.3/3.4 execution have not been tested here. The connection descriptor and sync API support replicas; that does not establish end-to-end replication correctness.
Live embedded replication, sync scheduling/read-your-writes behavior against a primary, encryption, Linux native execution, and Ruby 3.3/3.4 execution have not been tested here. The connection descriptor and sync API support replicas; that does not establish end-to-end replication correctness. Hosted remote checks do not cover token expiry/rotation, custom TLS settings, or production load behavior.

## Known limits

Expand Down
6 changes: 6 additions & 0 deletions lib/sequel/adapters/turso.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen-string-literal: true

require_relative 'libsql'

# Route Sequel.connect('turso://...') through the same adapter and pool.
Sequel.synchronize { Sequel::ADAPTER_MAP[:turso] = Sequel::LibSQL::Database }
9 changes: 6 additions & 3 deletions lib/sequel/libsql/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ def initialize(db, options)
@options = options
path, url = location
validate_url(url)
# Turso Cloud now supplies turso:// URLs. The pinned native driver
# predates that alias but supports the equivalent HTTPS endpoint.
url = url.sub(/\Aturso:/i, 'https:') if url
@native_options = {:path => path, :url => url}
add_credentials
add_replication_options
Expand All @@ -37,7 +40,7 @@ def location
database = @options[:database]
database = nil if database.to_s.empty?
url = @options[:url]
if database && /\A(?:libsql|https?):\/\//i.match?(database.to_s)
if database && /\A(?:turso|libsql|https?):\/\//i.match?(database.to_s)
raise Sequel::Error, 'specify the remote URL with database or url, not both' if url && url != database
url = database
database = nil
Expand All @@ -54,8 +57,8 @@ def location
def validate_url(url)
if url
uri = URI.parse(url.to_s)
unless %w[libsql https http].include?(uri.scheme) && uri.host && !uri.userinfo && !uri.fragment && !uri.query
raise Sequel::Error, 'libSQL URL must use libsql, https, or http with a host and no credentials, query, or fragment'
unless %w[turso libsql https http].include?(uri.scheme) && uri.host && !uri.userinfo && !uri.fragment && !uri.query
raise Sequel::Error, 'libSQL URL must use turso, libsql, https, or http with a host and no credentials, query, or fragment'
end
elsif @options[:auth_token]
raise Sequel::Error, 'auth_token requires a remote URL'
Expand Down
21 changes: 21 additions & 0 deletions test/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,27 @@ def test_uri_is_a_remote_url
db.disconnect if db
end

def test_turso_urls_use_https_without_becoming_local_files
remote = configuration(:database => 'turso://example.turso.io', :auth_token => 'test-only')
assert_equal 'https://example.turso.io', remote.native_options[:url]
assert_nil remote.native_options[:path]
assert remote.remote?
replica = configuration(:url => 'turso://example.turso.io', :path => 'replica.db')
assert replica.replica?
assert_equal 'https://example.turso.io', replica.native_options[:url]
assert_raises(Sequel::Error){configuration(:database => 'turso://user:secret@example.com')}
assert_raises(Sequel::Error){configuration(:database => 'turso://example.com?auth_token=secret')}
assert_raises(Sequel::Error){configuration(:database => 'turso://example.com#secret')}
end

def test_turso_uri_selects_the_libsql_adapter
db = Sequel.connect('turso://example.turso.io', :test => false, :auth_token => 'test-only')
assert_instance_of Sequel::LibSQL::Database, db
assert_equal 'https://example.turso.io', configuration(db.opts).native_options[:url]
ensure
db.disconnect if db
end

def test_remote_local_only_options_are_rejected
[:timeout, :case_sensitive_like, :synchronous, :temp_store, :auto_vacuum].each do |option|
assert_raises(Sequel::Error){configuration(:url => 'libsql://example.com', option => 1)}
Expand Down