API Reference
Stoolap API Reference
This document provides a comprehensive reference for the Stoolap API, including the new simplified API and the standard database/sql driver.
Simplified API (stoolap package)
The simplified API provides direct access to Stoolap functionality without requiring the database/sql
package.
Opening a Database
import "github.com/stoolap/stoolap"
// Open an in-memory database
db, err := stoolap.Open("memory://")
// Open a persistent database
db, err := stoolap.Open("file:///path/to/database")
Important: Only ONE engine instance can exist per DSN for the entire application lifetime. Subsequent calls to Open()
with the same DSN return the existing instance.
Database Methods
Basic Operations
// Execute a query that doesn't return rows
result, err := db.Exec(ctx, "CREATE TABLE users (id INT, name TEXT)")
// Query data
rows, err := db.Query(ctx, "SELECT * FROM users WHERE id = ?", 1)
defer rows.Close()
// Query single row
row := db.QueryRow(ctx, "SELECT name FROM users WHERE id = ?", 1)
var name string
err := row.Scan(&name)
// Close the database
err := db.Close()
Transactions
// Begin a transaction with default isolation level (READ COMMITTED)
tx, err := db.Begin()
// Begin a transaction with specific isolation level
import "database/sql"
opts := &sql.TxOptions{
Isolation: sql.LevelSnapshot,
}
tx, err := db.BeginTx(ctx, opts)
// Execute queries in transaction
_, err = tx.ExecContext(ctx, "INSERT INTO users VALUES (?, ?)", 1, "Alice")
_, err = tx.ExecContext(ctx, "UPDATE users SET name = ? WHERE id = ?", "Bob", 1)
// Commit or rollback
err = tx.Commit()
// or
err = tx.Rollback()
Prepared Statements
// Prepare a statement
stmt, err := db.Prepare("INSERT INTO users VALUES (?, ?)")
defer stmt.Close()
// Execute prepared statement
_, err = stmt.ExecContext(ctx, 1, "Alice")
_, err = stmt.ExecContext(ctx, 2, "Bob")
Advanced Features
// Get the underlying storage engine
engine := db.Engine()
// Get the SQL executor (for advanced usage)
executor := db.Executor()
// Enable vectorized execution mode
executor.EnableVectorizedMode()
// Check if vectorized mode is enabled
isEnabled := executor.IsVectorizedModeEnabled()
// Get default isolation level
level := executor.GetDefaultIsolationLevel()
Working with Results
Rows Interface
rows, err := db.Query(ctx, "SELECT id, name FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// Get column names
columns := rows.Columns()
// Iterate through results
for rows.Next() {
var id int
var name string
err := rows.Scan(&id, &name)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %d - %s\n", id, name)
}
Row Interface
row := db.QueryRow(ctx, "SELECT COUNT(*) FROM users")
var count int
err := row.Scan(&count)
if err == sql.ErrNoRows {
fmt.Println("No rows found")
} else if err != nil {
log.Fatal(err)
}
Standard database/sql Driver
Stoolap also provides a standard Go database/sql driver for compatibility.
Basic Usage
import (
"database/sql"
_ "github.com/stoolap/stoolap/pkg/driver"
)
// Open database
db, err := sql.Open("stoolap", "memory://")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Use standard database/sql methods
_, err = db.Exec("CREATE TABLE users (id INT, name TEXT)")
rows, err := db.Query("SELECT * FROM users")
Connection Strings
Both APIs support the same connection string formats:
memory://
- In-memory databasefile:///path/to/db
- Persistent databasefile:///path/to/db?sync_mode=full&snapshot_interval=60
- With options
Transaction Isolation Levels
Stoolap supports two isolation levels:
- READ COMMITTED (default)
- No read locks
- High concurrency
- Suitable for most OLTP workloads
- SNAPSHOT
- Write-write conflict detection
- Prevents lost updates
- Serialized commits for correctness
Error Handling
Common errors you might encounter:
import "database/sql"
// Check for no rows
err := row.Scan(&value)
if err == sql.ErrNoRows {
// Handle no rows case
}
// Check for constraint violations
if err != nil && strings.Contains(err.Error(), "UNIQUE constraint failed") {
// Handle unique constraint violation
}
// Check for transaction conflicts (SNAPSHOT isolation)
if err != nil && strings.Contains(err.Error(), "write-write conflict") {
// Retry transaction
}
Best Practices
- Always close resources: Use
defer
to ensure rows, statements, and transactions are closed - Use contexts: Pass contexts for cancellation and timeout support
- Handle conflicts: When using SNAPSHOT isolation, be prepared to retry on conflicts
- Singleton pattern: Remember that only one engine instance exists per DSN
- Parameter binding: Always use parameter placeholders (?) to prevent SQL injection
Example: Complete Application
package main
import (
"context"
"fmt"
"log"
"github.com/stoolap/stoolap"
)
func main() {
// Open database
db, err := stoolap.Open("memory://")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ctx := context.Background()
// Create schema
_, err = db.Exec(ctx, `
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`)
if err != nil {
log.Fatal(err)
}
// Insert data in a transaction
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
stmt, err := tx.Prepare("INSERT INTO users (id, name, email) VALUES (?, ?, ?)")
if err != nil {
tx.Rollback()
log.Fatal(err)
}
users := []struct {
id int
name string
email string
}{
{1, "Alice", "alice@example.com"},
{2, "Bob", "bob@example.com"},
{3, "Charlie", "charlie@example.com"},
}
for _, u := range users {
_, err = stmt.ExecContext(ctx, u.id, u.name, u.email)
if err != nil {
tx.Rollback()
log.Fatal(err)
}
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
// Query data
rows, err := db.Query(ctx, "SELECT id, name, email FROM users ORDER BY id")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
fmt.Println("Users:")
for rows.Next() {
var id int
var name, email string
err := rows.Scan(&id, &name, &email)
if err != nil {
log.Fatal(err)
}
fmt.Printf(" %d: %s <%s>\n", id, name, email)
}
}