libsql-client-ts icon indicating copy to clipboard operation
libsql-client-ts copied to clipboard

Add config-based database attach API

Open enki opened this issue 2 months ago • 2 comments

Implements config-based ATTACH API with explicit attach()/detach() methods.

This supersedes #327 and implements the config-based approach suggested by maintainers in the previous PR discussion.

Changes

Core API (libsql-core)

  • AttachConfig interface with alias and path fields
  • attach?: AttachConfig[] option in createClient() config for static attachments
  • client.attach(alias, path) and client.detach(alias) methods for dynamic attachments

Implementation (Sqlite3Client)

  • Config-based static attachments applied at client creation
  • Explicit attach/detach methods for runtime attachment
  • Automatic persistence across transaction() and reconnect()
  • Graceful handling of missing databases (logs warnings, doesn't crash)
  • Read-only mode support via file:///path?mode=ro URIs

Remote Clients (HTTP/WebSocket)

  • Stub implementations that throw ATTACH_NOT_SUPPORTED error
  • ATTACH only works with local SQLite files

Tests

  • 13 comprehensive tests covering all use cases
  • Config-based attachments
  • Explicit attach/detach methods
  • Persistence across transactions and reconnects
  • Read-only mode
  • Cross-database JOINs
  • Error handling (duplicate aliases, missing files)
  • Backward compatibility

Design Principles

Zero overhead: No SQL parsing or regex matching. Users explicitly control when and what to attach.

Persistence: Attachments survive transaction() (which nulls the connection) and reconnect() by re-applying from stored config.

Unified storage: Single internal array stores both config and explicit attachments, ensuring consistent behavior.

Addresses feedback from #327

  • ✅ Zero overhead (no SQL parsing)
  • ✅ User-controlled explicit API
  • ✅ Supports both static (config) and dynamic (explicit) attachments
  • ✅ Read-only mode for preventing write locks
  • ✅ Graceful degradation (missing DBs log warnings)

Example Usage

Static attachments:

const client = createClient({
  url: 'file:///path/to/main.db',
  attach: [
    { alias: 'archive', path: 'file:///path/to/archive.db?mode=ro' },
    { alias: 'analytics', path: 'file:///path/to/analytics.db' }
  ]
});

// Attachments persist across transactions
const tx = await client.transaction();
await tx.execute('SELECT * FROM archive.old_messages');

Dynamic attachments:

const client = createClient({ url: 'file:///path/to/main.db' });

// Attach at runtime
await client.attach('logs', 'file:///path/to/logs.db?mode=ro');

// Use in queries
await client.execute('SELECT * FROM logs.entries WHERE date > ?', [yesterday]);

// Detach when done
await client.detach('logs');

Closes #327

enki avatar Oct 24 '25 08:10 enki

@penberg this supersedes #327 based on your suggestions - can you review?

enki avatar Oct 30 '25 19:10 enki

Hey @enki! The API looks great. Let's get the build errors fixed and happy to merge

penberg avatar Nov 10 '25 12:11 penberg