zcatsql/docs/API.md
reugenio 7742f44667 v0.4: Fase 3A complete - Blob I/O, Hooks, Aggregate Functions
New features:
- Blob I/O: Incremental read/write for large BLOBs
  - Blob.open(), close(), deinit()
  - Blob.read(), write() with offset support
  - Blob.bytes(), reopen(), readAll()
- Hooks: Monitor database changes
  - setCommitHook() - called on transaction commit
  - setRollbackHook() - called on transaction rollback
  - setUpdateHook() - called on INSERT/UPDATE/DELETE
  - clearHooks() - remove all hooks
  - UpdateOperation enum (insert, update, delete)
- Aggregate Functions: Custom multi-row aggregates
  - createAggregateFunction(name, num_args, step_fn, final_fn)
  - AggregateContext with getAggregateContext() for state management
  - Support for setNull/Int/Float/Text/Blob/Error results

Documentation:
- Updated docs/API.md to v0.4 with new features and examples
- Updated docs/CGO_PARITY_ANALYSIS.md - Fase 3A marked complete
- Updated CLAUDE.md to v0.4 with all new implementations

Tests: 28 total (8 new tests for Fase 3A features)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 18:02:01 +01:00

16 KiB

zsqlite - API Reference

Version: 0.4 Ultima actualizacion: 2025-12-08

Quick Reference

const sqlite = @import("zsqlite");

// Abrir base de datos
var db = try sqlite.openMemory();          // In-memory
var db = try sqlite.open("file.db");       // Archivo
defer db.close();

// SQL directo
try db.exec("CREATE TABLE ...");

// Prepared statement
var stmt = try db.prepare("SELECT * FROM users WHERE id = ?");
defer stmt.finalize();
try stmt.bindInt(1, 42);
while (try stmt.step()) {
    const name = stmt.columnText(1);
}

// Named parameters
var stmt = try db.prepare("INSERT INTO users VALUES (:name, :age)");
try stmt.bindTextNamed(":name", "Alice");
try stmt.bindIntNamed(":age", 30);

// Transacciones
try db.begin();
// ... operaciones ...
try db.commit();  // o db.rollback()

// Savepoints
try db.savepoint(allocator, "sp1");
try db.rollbackTo(allocator, "sp1");
try db.release(allocator, "sp1");

// Backup
try sqlite.backupToFile(&db, "backup.db");
var restored = try sqlite.loadFromFile("backup.db");

// User-defined functions
try db.createScalarFunction("double", 1, myDoubleFunc);
try db.createAggregateFunction("sum_squares", 1, stepFn, finalFn);

// Custom collations
try db.createCollation("NOCASE2", myCaseInsensitiveCompare);

// Blob I/O
var blob = try sqlite.Blob.open(&db, "main", "table", "column", rowid, true);
defer blob.deinit();
try blob.write(data, 0);
try blob.read(&buffer, 0);

// Hooks
try db.setCommitHook(myCommitHook);
try db.setUpdateHook(myUpdateHook);

Funciones de Modulo

open

pub fn open(path: [:0]const u8) Error!Database

Abre una base de datos SQLite.

Parametros:

  • path: Ruta al archivo. Usar ":memory:" para base de datos en memoria.

Retorna: Database o error.


openMemory

pub fn openMemory() Error!Database

Abre una base de datos en memoria.


version / versionNumber

pub fn version() []const u8
pub fn versionNumber() i32

Retorna la version de SQLite como string ("3.47.2") o numero (3047002).


backupDatabase

pub fn backupDatabase(dest_db: *Database, source_db: *Database) Error!void

Copia una base de datos completa a otra.


backupToFile

pub fn backupToFile(source_db: *Database, path: [:0]const u8) Error!void

Guarda una base de datos a un archivo.


loadFromFile

pub fn loadFromFile(path: [:0]const u8) Error!Database

Carga una base de datos desde archivo a memoria.


Database

Apertura y Cierre

Funcion Descripcion
open(path) Abre conexion (read-write, create)
openWithFlags(path, flags) Abre con flags especificos
close() Cierra la conexion

Ejecucion SQL

Funcion Descripcion
exec(sql) Ejecuta SQL sin resultados
execAlloc(alloc, sql) exec con string runtime
prepare(sql) Crea prepared statement
prepareAlloc(alloc, sql) prepare con string runtime

Transacciones

Funcion Descripcion
begin() Inicia transaccion (DEFERRED)
beginImmediate() Inicia con lock inmediato
beginExclusive() Inicia con lock exclusivo
commit() Confirma transaccion
rollback() Revierte transaccion

Savepoints

pub fn savepoint(self: *Database, allocator: Allocator, name: []const u8) !void
pub fn release(self: *Database, allocator: Allocator, name: []const u8) !void
pub fn rollbackTo(self: *Database, allocator: Allocator, name: []const u8) !void

Savepoints permiten transacciones anidadas.

Ejemplo:

try db.begin();
try db.savepoint(alloc, "sp1");
try db.exec("INSERT ...");
try db.rollbackTo(alloc, "sp1");  // Revierte INSERT
try db.release(alloc, "sp1");
try db.commit();

Configuracion

Funcion Descripcion
setForeignKeys(enabled) Habilita/deshabilita FKs
setBusyTimeout(ms) Timeout en ms para locks
setJournalMode(alloc, mode) "WAL", "DELETE", etc
setSynchronous(alloc, mode) "OFF", "NORMAL", "FULL"
enableWalMode(alloc) WAL + NORMAL sync

ATTACH/DETACH

pub fn attach(self: *Database, alloc: Allocator, path: []const u8, schema: []const u8) !void
pub fn attachMemory(self: *Database, alloc: Allocator, schema: []const u8) !void
pub fn detach(self: *Database, alloc: Allocator, schema: []const u8) !void
pub fn listDatabases(self: *Database, alloc: Allocator) ![][]const u8
pub fn freeDatabaseList(alloc: Allocator, list: [][]const u8) void

Ejemplo:

try db.attachMemory(alloc, "cache");
try db.exec("CREATE TABLE cache.items (...)");
// SELECT * FROM cache.items
try db.detach(alloc, "cache");

User-Defined Functions

pub fn createScalarFunction(
    self: *Database,
    name: [:0]const u8,
    num_args: i32,
    func: ScalarFn,
) !void

pub fn removeFunction(self: *Database, name: [:0]const u8, num_args: i32) Error!void

Ejemplo:

fn myDouble(ctx: FunctionContext, args: []const FunctionValue) void {
    if (args[0].isNull()) {
        ctx.setNull();
        return;
    }
    ctx.setInt(args[0].asInt() * 2);
}

try db.createScalarFunction("double", 1, myDouble);
// SELECT double(value) FROM table

Custom Collations

pub fn createCollation(self: *Database, name: [:0]const u8, func: CollationFn) !void
pub fn removeCollation(self: *Database, name: [:0]const u8) Error!void

Ejemplo:

fn reverseOrder(a: []const u8, b: []const u8) i32 {
    return -std.mem.order(u8, a, b);
}

try db.createCollation("REVERSE", reverseOrder);
// SELECT * FROM table ORDER BY name COLLATE REVERSE

Utilidades

Funcion Descripcion
lastInsertRowId() Ultimo rowid insertado
changes() Filas modificadas (ultimo stmt)
totalChanges() Total filas desde conexion
errorMessage() Mensaje de error reciente
errorCode() Codigo de error
extendedErrorCode() Codigo extendido
interrupt() Interrumpe operacion
isReadOnly(db_name) Si DB es readonly
filename(db_name) Ruta del archivo

Statement

Ciclo de Vida

Funcion Descripcion
finalize() Libera el statement
reset() Resetea para re-ejecucion
clearBindings() Limpia parametros
step() Ejecuta un paso (true=hay fila)

Metadata

Funcion Descripcion
sql() Texto SQL del statement
isReadOnly() Si es SELECT
parameterCount() Numero de parametros
parameterIndex(name) Indice de parametro named
parameterName(index) Nombre de parametro

Bind Parameters (1-indexed)

Funcion Descripcion
bindNull(idx) NULL
bindInt(idx, val) i64
bindFloat(idx, val) f64
bindText(idx, val) []const u8
bindBlob(idx, val) []const u8
bindBool(idx, val) bool (como 0/1)
bindZeroblob(idx, size) Blob de ceros

Named Parameters

Funcion Descripcion
bindNullNamed(name) :name, @name, $name
bindIntNamed(name, val)
bindFloatNamed(name, val)
bindTextNamed(name, val)
bindBlobNamed(name, val)
bindBoolNamed(name, val)

Column Access (0-indexed)

Funcion Descripcion
columnCount() Numero de columnas
columnName(idx) Nombre
columnType(idx) ColumnType enum
columnInt(idx) i64
columnFloat(idx) f64
columnText(idx) ?[]const u8
columnBlob(idx) ?[]const u8
columnBool(idx) bool
columnIsNull(idx) bool
columnBytes(idx) Tamano en bytes
columnDeclType(idx) Tipo declarado

Backup

pub const Backup = struct {
    pub fn init(dest: *Database, dest_name: [:0]const u8,
                source: *Database, source_name: [:0]const u8) Error!Backup
    pub fn initMain(dest: *Database, source: *Database) Error!Backup
    pub fn step(self: *Backup, n_pages: i32) Error!bool
    pub fn stepAll(self: *Backup) Error!void
    pub fn remaining(self: *Backup) i32
    pub fn pageCount(self: *Backup) i32
    pub fn progress(self: *Backup) u8  // 0-100
    pub fn finish(self: *Backup) Error!void
    pub fn deinit(self: *Backup) void
};

Ejemplo con progreso:

var backup = try Backup.initMain(&dest_db, &source_db);
defer backup.deinit();

while (try backup.step(100)) {
    std.debug.print("Progress: {}%\n", .{backup.progress()});
}

User-Defined Functions

FunctionContext

pub const FunctionContext = struct {
    pub fn setNull(self: Self) void
    pub fn setInt(self: Self, value: i64) void
    pub fn setFloat(self: Self, value: f64) void
    pub fn setText(self: Self, value: []const u8) void
    pub fn setBlob(self: Self, value: []const u8) void
    pub fn setError(self: Self, msg: []const u8) void
};

FunctionValue

pub const FunctionValue = struct {
    pub fn getType(self: Self) ColumnType
    pub fn isNull(self: Self) bool
    pub fn asInt(self: Self) i64
    pub fn asFloat(self: Self) f64
    pub fn asText(self: Self) ?[]const u8
    pub fn asBlob(self: Self) ?[]const u8
};

ScalarFn Type

pub const ScalarFn = *const fn (ctx: FunctionContext, args: []const FunctionValue) void;

Types

OpenFlags

pub const OpenFlags = struct {
    read_only: bool = false,
    read_write: bool = true,
    create: bool = true,
    uri: bool = false,
    memory: bool = false,
    no_mutex: bool = false,
    full_mutex: bool = false,
};

ColumnType

pub const ColumnType = enum {
    integer,
    float,
    text,
    blob,
    null_value,
};

CollationFn

pub const CollationFn = *const fn (a: []const u8, b: []const u8) i32;

Retorna: negativo si a < b, cero si a == b, positivo si a > b.

Error

Mapeo completo de codigos SQLite:

Error Descripcion
SqliteError Error generico
Busy Database bloqueada
Locked Tabla bloqueada
Constraint Violacion de constraint
OutOfMemory Sin memoria
IoError Error de I/O
Corrupt DB corrupta
CantOpen No se puede abrir
ReadOnly DB es readonly
Range Parametro fuera de rango
... (25+ errores mapeados)

Ejemplos Completos

CRUD con Named Parameters

var db = try sqlite.openMemory();
defer db.close();

try db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");

// Insert con named params
var insert = try db.prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
defer insert.finalize();

try insert.bindTextNamed(":name", "Alice");
try insert.bindIntNamed(":age", 30);
_ = try insert.step();

// Query
var query = try db.prepare("SELECT * FROM users WHERE age > :min_age");
defer query.finalize();

try query.bindIntNamed(":min_age", 25);
while (try query.step()) {
    const name = query.columnText(1) orelse "(null)";
    std.debug.print("User: {s}\n", .{name});
}

Backup con Progreso

var source = try sqlite.open("production.db");
defer source.close();

var dest = try sqlite.open("backup.db");
defer dest.close();

var backup = try sqlite.Backup.initMain(&dest, &source);
defer backup.deinit();

while (try backup.step(100)) {
    const pct = backup.progress();
    std.debug.print("\rBackup: {d}%", .{pct});
}
std.debug.print("\nBackup complete!\n", .{});

UDF: String Length

fn strLen(ctx: sqlite.FunctionContext, args: []const sqlite.FunctionValue) void {
    if (args.len != 1 or args[0].isNull()) {
        ctx.setNull();
        return;
    }
    if (args[0].asText()) |text| {
        ctx.setInt(@intCast(text.len));
    } else {
        ctx.setNull();
    }
}

try db.createScalarFunction("strlen", 1, strLen);
// SELECT strlen(name) FROM users

Collation: Case-Insensitive

fn caseInsensitive(a: []const u8, b: []const u8) i32 {
    var i: usize = 0;
    while (i < a.len and i < b.len) : (i += 1) {
        const ca = std.ascii.toLower(a[i]);
        const cb = std.ascii.toLower(b[i]);
        if (ca < cb) return -1;
        if (ca > cb) return 1;
    }
    if (a.len < b.len) return -1;
    if (a.len > b.len) return 1;
    return 0;
}

try db.createCollation("ICASE", caseInsensitive);
// SELECT * FROM users ORDER BY name COLLATE ICASE

Aggregate Function: Sum of Squares

const SumState = struct {
    total: i64 = 0,
};

fn sumSquaresStep(ctx: sqlite.AggregateContext, args: []const sqlite.FunctionValue) void {
    const state = ctx.getAggregateContext(SumState) orelse return;
    if (args.len > 0 and !args[0].isNull()) {
        const val = args[0].asInt();
        state.total += val * val;
    }
}

fn sumSquaresFinal(ctx: sqlite.AggregateContext) void {
    const state = ctx.getAggregateContext(SumState) orelse {
        ctx.setNull();
        return;
    };
    ctx.setInt(state.total);
}

try db.createAggregateFunction("sum_squares", 1, sumSquaresStep, sumSquaresFinal);
// SELECT sum_squares(value) FROM numbers => 1+4+9+16+25 = 55

Blob I/O: Incremental Read/Write

// Insert placeholder blob
try db.exec("INSERT INTO files (data) VALUES (zeroblob(1024))");
const rowid = db.lastInsertRowId();

// Open for writing
var blob = try sqlite.Blob.open(&db, "main", "files", "data", rowid, true);
defer blob.deinit();

// Write data
try blob.write("Hello, World!", 0);

// Read data
var buffer: [100]u8 = undefined;
try blob.read(&buffer, 0);

Hooks: Monitor Database Changes

fn onCommit() bool {
    std.debug.print("Transaction committed!\n", .{});
    return true; // Allow commit
}

fn onUpdate(op: sqlite.UpdateOperation, db_name: []const u8, table: []const u8, rowid: i64) void {
    std.debug.print("{s} on {s}.{s} row {d}\n", .{
        @tagName(op), db_name, table, rowid
    });
}

try db.setCommitHook(onCommit);
try db.setUpdateHook(onUpdate);
// ... operations will trigger hooks ...
db.clearHooks(); // Remove all hooks

Blob

pub const Blob = struct {
    pub fn open(db: *Database, schema: [:0]const u8, table: [:0]const u8,
                column: [:0]const u8, rowid: i64, writable: bool) Error!Blob
    pub fn openAlloc(db: *Database, allocator: Allocator, ...) !Blob
    pub fn close(self: *Blob) Error!void
    pub fn deinit(self: *Blob) void
    pub fn bytes(self: *Blob) i32
    pub fn read(self: *Blob, buffer: []u8, offset: i32) Error!void
    pub fn write(self: *Blob, data: []const u8, offset: i32) Error!void
    pub fn reopen(self: *Blob, rowid: i64) Error!void
    pub fn readAll(self: *Blob, allocator: Allocator) ![]u8
};

Hooks

Types

pub const ZigCommitHookFn = *const fn () bool;
pub const ZigRollbackHookFn = *const fn () void;
pub const ZigUpdateHookFn = *const fn (
    operation: UpdateOperation,
    db_name: []const u8,
    table_name: []const u8,
    rowid: i64
) void;

pub const UpdateOperation = enum(i32) {
    insert,
    update,
    delete,
};

Database Methods

Method Description
setCommitHook(fn) Called when transaction commits
setRollbackHook(fn) Called when transaction rolls back
setUpdateHook(fn) Called on INSERT/UPDATE/DELETE
clearHooks() Remove all hooks

Aggregate Functions

Types

pub const AggregateStepFn = *const fn (ctx: AggregateContext, args: []const FunctionValue) void;
pub const AggregateFinalFn = *const fn (ctx: AggregateContext) void;

AggregateContext

pub const AggregateContext = struct {
    pub fn getAggregateContext(self: Self, comptime T: type) ?*T
    pub fn setNull(self: Self) void
    pub fn setInt(self: Self, value: i64) void
    pub fn setFloat(self: Self, value: f64) void
    pub fn setText(self: Self, value: []const u8) void
    pub fn setBlob(self: Self, value: []const u8) void
    pub fn setError(self: Self, msg: []const u8) void
};

Database Method

pub fn createAggregateFunction(
    self: *Database,
    name: [:0]const u8,
    num_args: i32,
    step_fn: AggregateStepFn,
    final_fn: AggregateFinalFn,
) !void

© zsqlite v0.4 - API Reference 2025-12-08