zcatsql/docs/ARCHITECTURE.md
reugenio 5e28cbe4bf refactor: modularize root.zig into specialized modules
Split monolithic root.zig (4200 lines) into 9 focused modules:
- c.zig: centralized @cImport for SQLite
- errors.zig: Error enum and resultToError
- types.zig: OpenFlags, ColumnType, Limit, enums
- database.zig: Database struct with all methods
- statement.zig: Statement struct with bindings/columns
- functions.zig: UDFs, hooks, and C callbacks
- backup.zig: Backup and Blob I/O
- pool.zig: ConnectionPool (thread-safe)
- root.zig: re-exports + tests (~1100 lines)

Total: ~3600 lines (74% reduction in root.zig)
All 47 tests passing.

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

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

405 lines
12 KiB
Markdown

# zsqlite - Arquitectura Tecnica
> **Version**: 0.6
> **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 │ │ Backup │ │ConnPool │ │
│ │ │ │ │ │ Blob │ │ Functions │ │
│ └────┬─────┘ └─────┬─────┘ └────┬─────┘ └─────┬─────┘ │
├───────┼──────────────┼─────────────┼──────────────┼────────┤
│ │ │ │ │ │
│ └──────────────┴─────────────┴──────────────┘ │
│ @cImport │
├─────────────────────────────────────────────────────────────┤
│ SQLite 3.47.2 (C) │
│ (amalgamation) │
└─────────────────────────────────────────────────────────────┘
```
## Componentes Principales
### 1. Database
Representa una conexion a una base de datos SQLite.
```zig
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.
```zig
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.
```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.
```zig
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.
```zig
pub const ColumnType = enum {
integer,
float,
text,
blob,
null_value,
};
```
### 6. Backup
API para copiar bases de datos con control de progreso.
```zig
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.
```zig
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:**
```zig
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.
```zig
pub const CollationFn = *const fn (a: []const u8, b: []const u8) i32;
```
**Patron de uso:**
```zig
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:
```zig
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:
```zig
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
```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
```zig
var db = try sqlite.openMemory();
defer db.close();
var stmt = try db.prepare("SELECT * FROM users");
defer stmt.finalize();
```
### Patron Transaccion con errdefer
```zig
try db.begin();
errdefer db.rollback() catch {};
try db.exec("INSERT ...");
try db.exec("UPDATE ...");
try db.commit();
```
### Iteracion de Resultados
```zig
while (try stmt.step()) {
const id = stmt.columnInt(0);
const name = stmt.columnText(1) orelse "(null)";
// procesar fila
}
```
## Decisiones de Diseno
### 1. Estructura Modular
El codigo esta organizado en modulos especializados:
```
src/
├── root.zig # Re-exports publicos + tests (~1100 lineas)
├── c.zig # @cImport centralizado (24 lineas)
├── errors.zig # Error enum y resultToError (142 lineas)
├── types.zig # OpenFlags, ColumnType, enums (154 lineas)
├── database.zig # Database struct (795 lineas)
├── statement.zig # Statement struct (378 lineas)
├── functions.zig # UDFs, hooks, callbacks (567 lineas)
├── backup.zig # Backup y Blob I/O (292 lineas)
└── pool.zig # ConnectionPool (151 lineas)
```
**Total**: ~3600 lineas (vs 4200 monoliticas anteriores)
Cada modulo tiene una responsabilidad clara:
- **c.zig**: Unico punto de @cImport para SQLite
- **errors.zig**: Mapeo completo de errores SQLite -> Zig
- **types.zig**: Tipos compartidos (flags, enums)
- **database.zig**: Conexion y operaciones de base de datos
- **statement.zig**: Prepared statements y bindings
- **functions.zig**: Funciones definidas por usuario y hooks
- **backup.zig**: API de backup y blob streaming
- **pool.zig**: Pool de conexiones thread-safe
- **root.zig**: Re-exporta API publica + contiene tests
### 2. Error Union vs Nullable
- Operaciones que pueden fallar: `Error!T`
- Operaciones de lectura que pueden ser NULL: `?T`
```zig
// 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
### Completado: Modularizacion
La estructura modular ya esta implementada (ver seccion "Estructura Modular").
### Siguiente: Optimizaciones
- [ ] Reducir duplicacion en funciones de binding
- [ ] Crear helpers de test para reducir boilerplate
- [ ] Considerar vtable API si se necesitan mas extensiones
---
**© zsqlite - Arquitectura Tecnica**
*2025-12-08*