Skip to content

Repository files navigation

Ring LibSQL

LibSQL client library for Ring with SQLite compatibility and support for local, remote, and embedded replica databases.

✨ Features

  • Full SQLite-compatible database operations
  • Local file-based databases (:memory: or file paths)
  • Remote database access with authentication
  • Embedded replica support for offline-first applications with sync capabilities
  • Prepared statements with parameter binding (int, float, string, blob, null)
  • Encryption support for both local and remote databases
  • WebPKI support for secure HTTPS connections
  • Transaction support and connection management
  • Cross-platform support (Windows, Linux, macOS, FreeBSD)

Note

This extension/library is built on top of the experimental c bindings of libsql.

📦 Installation

This package can be installed using the Ring Package Manager (RingPM):

ringpm install ring-libsql from ysdragon

💡 Usage

First, load the library in your Ring script:

load"libsql.ring"

Local Database (In-Memory)

load"libsql.ring"
# Openin-memorydatabase
db =newLibSQLdb.openExt(":memory:")
conn=db.connect()
# Createatableandinsertdataconn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
conn.execute("INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')")
conn.execute("INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')")
# Querydatarows=conn.query("SELECT * FROM users")
whileTruerow=rows.fetchRow()
ifisNull(row)
exitokdata=row.toList()
? "ID: " + data[1] + ", Name: " + data[2] + ", Email: " + data[3]
end
# Orfetchallatonceasassociativearrayresults=conn.query("SELECT * FROM users").fetchAllAssoc()
foruserinresults
? user # Prints [["id", 1], ["name", "Alice"], ["email", "alice@example.com"]]
next
# Cleanupconn.disconnect()
db.close()

Local Database (File)

load"libsql.ring"
# Openfile-baseddatabase
db =newLibSQLdb.openFile("mydata.db")
conn=db.connect()
# Usethedatabaseconn.execute("CREATE TABLE IF NOT EXISTS products (id INTEGER, name TEXT, price REAL)")
conn.execute("INSERT INTO products VALUES (1, 'Laptop', 999.99)")
# Cleanupconn.disconnect()
db.close()

Remote Database (Turso or Self-Hosted)

load"libsql.ring"
# Option1: ConnecttoTursohosteddatabase
db =newLibSQLdb.openRemote("libsql://your-database.turso.io", "your-auth-token")
conn=db.connect()
# Option2: Connecttoyourself-hostedLibSQLserver
# Canbelocalhost, VPS, cloudserver, or anyremotemachine
# db =newLibSQL
# db.openRemote("http://your-server.com:8080", "optional-auth-token")
# conn=db.connect()
# Executequeriesconn.execute("INSERT INTO logs (message, timestamp) VALUES ('Hello from Ring!', datetime('now'))")
# Queryremotedatarows=conn.query("SELECT * FROM logs ORDER BY timestamp DESC LIMIT 10")
logs=rows.fetchAllAssoc()
forloginlogs
? lognext
# Cleanupconn.disconnect()
db.close()

Embedded Replica (Offline-First with Sync)

load"libsql.ring"
# Openembeddedreplicawithsyncconfigurationconfig= [
:db_path="local_replica.db",
:primary_url="libsql://your-database.turso.io",
:auth_token="your-auth-token",
:read_your_writes=1,
:sync_interval=60 # Auto-syncevery60seconds
]
db =newLibSQLdb.openSyncWithConfig(config)
conn=db.connect()
# Workwithlocal replica (worksoffline)
conn.execute("INSERT INTO tasks (title, done) VALUES ('Learn Ring', 0)")
conn.execute("INSERT INTO tasks (title, done) VALUES ('Build App', 0)")
# Manuallysyncwithremotedb.sync()
# GetsyncstatisticssyncInfo=db.sync2()
? "Synced frames: " + syncInfo[2]
# Cleanupconn.disconnect()
db.close()

Prepared Statements with Parameter Binding

load"libsql.ring"
db =newLibSQLdb.openExt(":memory:")
conn=db.connect()
conn.execute("CREATE TABLE users (id INTEGER, name TEXT, score REAL, active INTEGER)")
# Preparestatementstmt=conn.prepare("INSERT INTO users (id, name, score, active) VALUES (?, ?, ?, ?)")
# Method1: Bindindividuallywithtype-specificmethodsstmt.bindInt(1, 1)
.bindString(2, "Alice")
.bindFloat(3, 95.5)
.bindInt(4, 1)
.execute()
# Method2: Bindallparametersatonce (auto-detectstypes)
stmt.reset()
.bindParams([2, "Bob", 87.3, 1])
.execute()
# Method3: Usesmartbind (handlesnulls and auto-types)
stmt.reset()
.bind(1, 3)
.bind(2, "Charlie")
.bind(3, 92.0)
.bind(4, null) # NULLvalue
.execute()
# QuerywithpreparedstatementsqueryStmt=conn.prepare("SELECT * FROM users WHERE score > ?")
queryStmt.bindFloat(1, 90.0)
results=queryStmt.query().fetchAllAssoc()
foruserinresults
? usernext
# Cleanupconn.disconnect()
db.close()

Database Encryption

load"libsql.ring"
# Embeddedreplicawith encryption (syncswithremoteserver)
db =newLibSQLdb.openSync(
"local_encrypted.db",
"libsql://your-database.turso.io", # RemoteURLrequired"your-auth-token",
1, # read_your_writes"my-encryption-key-32-bytes-long"
)
conn=db.connect()
conn.execute("CREATE TABLE IF NOT EXISTS secrets (id INTEGER, data TEXT)")
conn.execute("INSERT INTO secrets VALUES (1, 'Top Secret Data')")
# Syncencrypteddatawithremotedb.sync()
# Cleanupconn.disconnect()
db.close()

Working with Different Data Types

load"libsql.ring"
db =newLibSQLdb.openExt(":memory:")
conn=db.connect()
conn.execute("CREATE TABLE mixed_types (	id INTEGER,	name TEXT,	price REAL,	data BLOB,	nullable TEXT)")
# Insertwithdifferenttypesstmt=conn.prepare("INSERT INTO mixed_types VALUES (?, ?, ?, ?, ?)")
stmt.bindInt(1, 1)
.bindString(2, "Product")
.bindFloat(3, 29.99)
.bindBlob(4, "Binary Data Here")
.bindNull(5)
.execute()
# Fetchandaccessbytyperows=conn.query("SELECT * FROM mixed_types")
row=rows.fetchRow()
? "ID (int): " + row.getIntValue(1)
? "Name (string): " + row.getStringValue(2)
? "Price (float): " + row.getFloatValue(3)
? "Data (blob): " + row.getBlobValue(4)
nullableVal=row.getValue(5)
ifisNull(nullableVal)
? "Nullable: NULL"else
? "Nullable: " + nullableValok
# Cleanupconn.disconnect()
db.close()

📚 API Reference

LibSQL Class (Database)

The main database class for opening and managing database connections.

Opening Databases

  • openFile(path) - Open local file database
  • openExt(path) - Open database (:memory:, file path, or URL)
  • openRemote(url, auth_token) - Open remote database
  • openRemoteWithWebPKI(url, auth_token) - Open remote with WebPKI
  • openRemoteWithEncryption(url, auth_token, key) - Open remote with encryption
  • openSync(db_path, url, token, read_your_writes, key) - Open embedded replica
  • openSyncWithWebPKI(db_path, url, token, read_your_writes, key) - Open embedded replica with WebPKI
  • openSyncWithConfig(config_list) - Open with configuration list

Database Operations

  • connect() - Create connection, returns LibSQLConnection object
  • sync() - Manually sync embedded replica with remote
  • sync2() - Sync and return frame statistics [frame_no, frames_synced]
  • close() - Close database

LibSQLConnection Class (Connection)

Represents an active database connection for executing queries.

Query Execution

  • execute(sql) - Execute SQL without returning rows
  • query(sql) - Execute query, returns LibSQLRows object
  • prepare(sql) - Prepare statement, returns LibSQLStatement object
  • changes() - Get number of rows affected by last operation
  • lastInsertRowID() - Get last inserted row ID

Connection Management

  • disconnect() - Close connection
  • reset() - Reset connection state
  • loadExtension(path, entry_point) - Load SQLite extension
  • setReservedBytes(bytes) - Set reserved bytes for encryption
  • getReservedBytes() - Get reserved bytes

LibSQLStatement Class (Prepared Statement)

Prepared statement with parameter binding.

Parameter Binding

  • bindInt(index, value) - Bind integer (1-based index)
  • bindFloat(index, value) - Bind float (1-based index)
  • bindString(index, value) - Bind string (1-based index)
  • bindBlob(index, value) - Bind blob (1-based index)
  • bindNull(index) - Bind NULL (1-based index)
  • bind(index, value) - Smart bind (auto-detects type, 1-based index)
  • bindParams(params_list) - Bind all parameters from list (1-based)

Execution

  • execute() - Execute statement without returning rows
  • query() - Execute statement, returns LibSQLRows object
  • reset() - Reset statement for reuse

LibSQLRows Class (Result Set)

Represents query results.

Column Information

  • columnCount() - Get number of columns
  • columnName(index) - Get column name (1-based index)
  • columnNames() - Get list of all column names

Fetching Rows

  • fetchRow() - Fetch next row, returns LibSQLRow or null
  • fetchAll() - Fetch all rows as list of lists
  • fetchAllAssoc() - Fetch all rows as associative arrays

LibSQLRow Class (Row Data)

Represents a single result row.

Type-Specific Access

  • getIntValue(index) - Get integer value (1-based index)
  • getFloatValue(index) - Get float value
  • getStringValue(index) - Get string value
  • getBlobValue(index) - Get blob value
  • getType(index) - Get column type constant
  • getValue(index) - Get value with automatic type conversion

Row Conversion

  • toList() - Convert row to list of values
  • toAssoc() - Convert row to associative array [["col", val], ...]

Constants

  • LIBSQL_INT - Integer column type
  • LIBSQL_FLOAT - Float column type
  • LIBSQL_TEXT - Text column type
  • LIBSQL_BLOB - Blob column type
  • LIBSQL_NULL - NULL column type

Low-Level C Functions

For advanced users, all underlying C functions are also available: libsql_open_*, libsql_connect, libsql_execute, libsql_query, libsql_prepare, libsql_bind_*, libsql_next_row, libsql_get_*, etc.

🛠️ Development

If you wish to contribute to the development of Ring LibSQL or build it from the source, follow these steps.

Prerequisites

  • CMake: Version 3.16 or higher
  • C Compiler: A C compiler compatible with your platform (e.g., GCC, Clang, MSVC)
  • Rust & Cargo: Required to build libsql from source
  • Git: For cloning repositories
  • Ring Source Code: Ring language source code must be available on your machine

Build Steps

  1. Clone the Repository:

    git clone https://github.com/ysdragon/ring-libsql.git
    cd ring-libsql

    Note If you installed the library via RingPM, you can skip this step.

  2. Set the RING Environment Variable: This variable must point to the root directory of the Ring language source code.

    • Windows (Command Prompt):
      setRING=X:\path\to\ring
    • Windows (PowerShell):
      $env:RING="X:\path\to\ring"
    • Unix-like Systems (Linux, macOS or FreeBSD):
      export RING=/path/to/ring
  3. Configure with CMake: Create a build directory and run CMake from within it. CMake will automatically:

    • Clone libsql from GitHub if not found
    • Build libsql using Cargo
    mkdir build
    cd build
    cmake .. -DCMAKE_BUILD_TYPE=Release
  4. Build the Project: Compile the source code using the build toolchain configured by CMake.

    cmake --build .

    The compiled library will be available in the lib/<os>/<arch> directory.

🤝 Contributing

Contributions are always welcome! If you have suggestions for improvements or have identified a bug, please feel free to open an issue or submit a pull request.

📄 License

This project is licensed under the MIT License. See the LICENSE file for more details.

About

libSQL bindings for Ring

Topics

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages