zcatsql/docs/ARCHITECTURE.md
reugenio 532cf827f8 v0.3: Fase 2A+2B complete - Major feature additions
New features:
- Backup API: Backup struct with step(), progress(), remaining(), pageCount()
- Convenience functions: backupToFile(), loadFromFile(), backupDatabase()
- ATTACH/DETACH: attach(), attachMemory(), detach(), listDatabases()
- User-Defined Functions: createScalarFunction(), FunctionContext, FunctionValue
- Custom Collations: createCollation(), CollationFn, removeCollation()
- Named parameters: bindTextNamed(), bindIntNamed(), etc. (:name, @name, $name)
- Savepoints: savepoint(), release(), rollbackTo()
- Configuration: setBusyTimeout(), setJournalMode(), setSynchronous(), enableWalMode()
- Statement metadata: sql(), isReadOnly(), parameterCount(), parameterIndex()
- Transaction: beginExclusive()
- Utilities: errorCode(), extendedErrorCode(), interrupt(), isReadOnly(), filename()
- Additional bind/column: bindBool(), bindZeroblob(), columnBool(), columnBytes(), columnDeclType()

Documentation:
- Created docs/API.md - Complete API reference
- Created docs/ARCHITECTURE.md - Technical architecture
- Created docs/CGO_PARITY_ANALYSIS.md - Feature parity tracking with go-sqlite3
- Updated CLAUDE.md with all new features

All 20 tests passing.

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

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

11 KiB

zsqlite - Arquitectura Tecnica

Version: 0.3 Ultima actualizacion: 2025-12-08

Vision General

zsqlite es un wrapper de SQLite para Zig que compila SQLite amalgamation directamente en el binario. El objetivo es proveer una API idiomatica Zig mientras se mantiene acceso completo a las capacidades de SQLite.

┌─────────────────────────────────────────────────────────────┐
│                    Aplicacion Zig                           │
├─────────────────────────────────────────────────────────────┤
│                    zsqlite API                              │
│  ┌──────────┐  ┌───────────┐  ┌──────────┐  ┌───────────┐  │
│  │ Database │  │ Statement │  │  Error   │  │  Column   │  │
│  │          │  │           │  │ Mapping  │  │   Type    │  │
│  └────┬─────┘  └─────┬─────┘  └────┬─────┘  └─────┬─────┘  │
├───────┼──────────────┼─────────────┼──────────────┼────────┤
│       │              │             │              │         │
│       └──────────────┴─────────────┴──────────────┘         │
│                         @cImport                            │
├─────────────────────────────────────────────────────────────┤
│                    SQLite 3.47.2 (C)                        │
│                    (amalgamation)                           │
└─────────────────────────────────────────────────────────────┘

Componentes Principales

1. Database

Representa una conexion a una base de datos SQLite.

pub const Database = struct {
    handle: ?*c.sqlite3,

    pub fn open(path: [:0]const u8) Error!Self { ... }
    pub fn openWithFlags(path: [:0]const u8, flags: OpenFlags) Error!Self { ... }
    pub fn close(self: *Self) void { ... }
    pub fn exec(self: *Self, sql: [:0]const u8) Error!void { ... }
    pub fn prepare(self: *Self, sql: [:0]const u8) Error!Statement { ... }
    // ...
};

Responsabilidades:

  • Gestionar ciclo de vida de conexion SQLite
  • Ejecutar SQL directo (exec)
  • Crear prepared statements
  • Gestionar transacciones
  • Configurar pragmas (foreign keys, etc.)

2. Statement

Representa un prepared statement de SQLite.

pub const Statement = struct {
    handle: ?*c.sqlite3_stmt,
    db: *Database,

    pub fn finalize(self: *Self) void { ... }
    pub fn reset(self: *Self) Error!void { ... }
    pub fn step(self: *Self) Error!bool { ... }
    pub fn bindInt(self: *Self, index: u32, value: i64) Error!void { ... }
    pub fn bindTextNamed(self: *Self, name: [:0]const u8, value: []const u8) Error!void { ... }
    pub fn columnText(self: *Self, index: u32) ?[]const u8 { ... }
    // ...
};

Responsabilidades:

  • Bind de parametros posicionales (1-indexed)
  • Bind de parametros named (:name, @name, $name)
  • Ejecucion paso a paso (step)
  • Acceso a columnas de resultados
  • Metadata (sql, isReadOnly, parameterCount, etc.)
  • Reset para reutilizacion

3. Error

Mapeo de codigos de error SQLite a errores Zig.

pub const Error = error{
    SqliteError,
    InternalError,
    PermissionDenied,
    Busy,
    Locked,
    OutOfMemory,
    // ... 25+ errores mapeados
};

fn resultToError(result: c_int) Error { ... }

Diseno:

  • Cada codigo SQLite tiene su propio error Zig
  • Permite manejo especifico por tipo de error
  • SQLITE_ROW y SQLITE_DONE manejados especialmente en step()

4. OpenFlags

Configuracion para apertura de base de datos.

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,
};

5. ColumnType

Enum para tipos de columna SQLite.

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

6. Backup

API para copiar bases de datos con control de progreso.

pub const Backup = struct {
    handle: ?*c.sqlite3_backup,
    dest_db: *Database,
    source_db: *Database,

    pub fn init(dest: *Database, dest_name: [:0]const u8,
                source: *Database, source_name: [:0]const u8) Error!Self { ... }
    pub fn step(self: *Self, n_pages: i32) Error!bool { ... }
    pub fn stepAll(self: *Self) Error!void { ... }
    pub fn remaining(self: *Self) i32 { ... }
    pub fn pageCount(self: *Self) i32 { ... }
    pub fn progress(self: *Self) u8 { ... }
    pub fn finish(self: *Self) Error!void { ... }
    pub fn deinit(self: *Self) void { ... }
};

Responsabilidades:

  • Copiar base de datos pagina por pagina
  • Control granular del progreso
  • Permitir backups incrementales

7. User-Defined Functions

Soporte para funciones personalizadas en SQL.

pub const FunctionContext = struct {
    ctx: *c.sqlite3_context,

    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 { ... }
};

pub const FunctionValue = struct {
    value: *c.sqlite3_value,

    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 { ... }
};

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

Patron de uso:

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(5) => 10

8. Custom Collations

Soporte para ordenamiento personalizado.

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

Patron de uso:

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

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

Integracion con SQLite C

@cImport

Usamos @cImport para importar los headers de SQLite:

const c = @cImport({
    @cInclude("sqlite3.h");
});

Esto nos da acceso directo a:

  • c.sqlite3 - Handle de conexion
  • c.sqlite3_stmt - Handle de statement
  • c.sqlite3_open(), c.sqlite3_close(), etc.
  • Todas las constantes (c.SQLITE_OK, c.SQLITE_ROW, etc.)

SQLITE_TRANSIENT

Para strings y blobs, usamos c.SQLITE_TRANSIENT que indica a SQLite que debe copiar los datos:

const result = c.sqlite3_bind_text(
    self.handle,
    @intCast(index),
    value.ptr,
    @intCast(value.len),
    c.SQLITE_TRANSIENT,  // SQLite copia el string
);

Esto es necesario porque Zig puede mover/liberar la memoria del slice original.

Conversion de Indices

SQLite usa diferentes convenciones de indices:

  • Bind parameters: 1-indexed (SQLite convencion)
  • Column access: 0-indexed (SQLite convencion)

Mantenemos las mismas convenciones en la API publica para consistencia con documentacion SQLite.

Build System

build.zig

// Compilar SQLite como C source
zsqlite_mod.addCSourceFile(.{
    .file = b.path("vendor/sqlite3.c"),
    .flags = sqlite_flags,
});

// Flags de optimizacion
const sqlite_flags: []const []const u8 = &.{
    "-DSQLITE_DQS=0",
    "-DSQLITE_THREADSAFE=0",
    "-DSQLITE_DEFAULT_MEMSTATUS=0",
    // ...
};

Flags de Compilacion

Flag Proposito
SQLITE_DQS=0 Deshabilita double-quoted strings como identificadores
SQLITE_THREADSAFE=0 Single-threaded (mas rapido)
SQLITE_DEFAULT_MEMSTATUS=0 Sin tracking de memoria
SQLITE_ENABLE_FTS5 Full-text search habilitado
SQLITE_ENABLE_JSON1 Funciones JSON habilitadas
SQLITE_ENABLE_RTREE R-Tree para geospatial
SQLITE_OMIT_LOAD_EXTENSION Sin extensiones dinamicas (seguridad)

Patrones de Uso

Patron RAII con defer

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

var stmt = try db.prepare("SELECT * FROM users");
defer stmt.finalize();

Patron Transaccion con errdefer

try db.begin();
errdefer db.rollback() catch {};

try db.exec("INSERT ...");
try db.exec("UPDATE ...");

try db.commit();

Iteracion de Resultados

while (try stmt.step()) {
    const id = stmt.columnInt(0);
    const name = stmt.columnText(1) orelse "(null)";
    // procesar fila
}

Decisiones de Diseno

1. Todo en root.zig (por ahora)

Para v0.1, todo el codigo esta en un solo archivo. Cuando crezca significativamente (>400 lineas core), se fragmentara en:

  • database.zig
  • statement.zig
  • errors.zig
  • types.zig

2. Error Union vs Nullable

  • Operaciones que pueden fallar: Error!T
  • Operaciones de lectura que pueden ser NULL: ?T
// Puede fallar (error de SQLite)
pub fn open(path: [:0]const u8) Error!Self

// Puede ser NULL (columna NULL)
pub fn columnText(self: *Self, index: u32) ?[]const u8

3. Slices vs Null-Terminated

  • API publica acepta [:0]const u8 para strings SQL (null-terminated)
  • Variantes *Alloc aceptan []const u8 y agregan null terminator

4. Indices Consistentes con SQLite

Mantenemos las convenciones de SQLite:

  • Bind: 1-indexed
  • Columns: 0-indexed

Esto facilita traducir ejemplos de documentacion SQLite.

Roadmap Arquitectural

Fase 2: Modularizacion

Cuando el codigo crezca, fragmentar en modulos:

src/
├── root.zig        # Re-exports publicos
├── database.zig    # Database struct
├── statement.zig   # Statement struct
├── errors.zig      # Error types
├── types.zig       # OpenFlags, ColumnType
└── c.zig           # @cImport centralizado

Fase 3: Features Avanzadas

src/
├── ...
├── blob.zig        # Blob streaming
├── functions.zig   # User-defined functions
├── backup.zig      # Backup API
└── hooks.zig       # Update/commit hooks

© zsqlite - Arquitectura Tecnica 2025-12-08