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>
523 lines
15 KiB
Markdown
523 lines
15 KiB
Markdown
# zsqlite - SQLite Wrapper para Zig
|
|
|
|
> **Ultima actualizacion**: 2025-12-08
|
|
> **Lenguaje**: Zig 0.15.2
|
|
> **Estado**: v0.4 - Fase 3A completada
|
|
> **Inspiracion**: CGo go-sqlite3, SQLite C API
|
|
|
|
## Descripcion del Proyecto
|
|
|
|
**zsqlite** es un wrapper idiomatico de SQLite para Zig que compila SQLite amalgamation directamente en el binario, resultando en un ejecutable unico sin dependencias externas.
|
|
|
|
**Filosofia**:
|
|
- Zero dependencias runtime
|
|
- API idiomatica Zig (errores, allocators, iteradores)
|
|
- Binario unico y portable
|
|
- Compatible con bases de datos SQLite existentes
|
|
- Calidad open source (doc comments, codigo claro)
|
|
|
|
**Objetivo**: Ser el pilar para trabajar con databases en Zig con codigo 100% propio, replicando toda la funcionalidad de wrappers maduros como CGo go-sqlite3.
|
|
|
|
---
|
|
|
|
## Estado Actual del Proyecto
|
|
|
|
### Implementacion v0.4 (Fase 3A Completada)
|
|
|
|
| Componente | Estado | Archivo |
|
|
|------------|--------|---------|
|
|
| **Core** | | |
|
|
| Database open/close | ✅ | `src/root.zig` |
|
|
| Database open with flags | ✅ | `src/root.zig` |
|
|
| exec() SQL simple | ✅ | `src/root.zig` |
|
|
| execAlloc() runtime strings | ✅ | `src/root.zig` |
|
|
| Error mapping completo | ✅ | `src/root.zig` |
|
|
| **Prepared Statements** | | |
|
|
| prepare/finalize | ✅ | `src/root.zig` |
|
|
| prepareAlloc() runtime strings | ✅ | `src/root.zig` |
|
|
| reset/clearBindings | ✅ | `src/root.zig` |
|
|
| step() iteration | ✅ | `src/root.zig` |
|
|
| sql() / isReadOnly() | ✅ | `src/root.zig` |
|
|
| parameterCount/Index/Name | ✅ | `src/root.zig` |
|
|
| **Bind Parameters** | | |
|
|
| bindNull | ✅ | `src/root.zig` |
|
|
| bindInt (i64) | ✅ | `src/root.zig` |
|
|
| bindFloat (f64) | ✅ | `src/root.zig` |
|
|
| bindText | ✅ | `src/root.zig` |
|
|
| bindBlob | ✅ | `src/root.zig` |
|
|
| bindBool | ✅ | `src/root.zig` |
|
|
| bindZeroblob | ✅ | `src/root.zig` |
|
|
| **Named Parameters** | | |
|
|
| bindNullNamed | ✅ | `src/root.zig` |
|
|
| bindIntNamed | ✅ | `src/root.zig` |
|
|
| bindFloatNamed | ✅ | `src/root.zig` |
|
|
| bindTextNamed | ✅ | `src/root.zig` |
|
|
| bindBlobNamed | ✅ | `src/root.zig` |
|
|
| bindBoolNamed | ✅ | `src/root.zig` |
|
|
| **Column Access** | | |
|
|
| columnCount | ✅ | `src/root.zig` |
|
|
| columnName | ✅ | `src/root.zig` |
|
|
| columnType | ✅ | `src/root.zig` |
|
|
| columnInt | ✅ | `src/root.zig` |
|
|
| columnFloat | ✅ | `src/root.zig` |
|
|
| columnText | ✅ | `src/root.zig` |
|
|
| columnBlob | ✅ | `src/root.zig` |
|
|
| columnIsNull | ✅ | `src/root.zig` |
|
|
| columnBool | ✅ | `src/root.zig` |
|
|
| columnBytes | ✅ | `src/root.zig` |
|
|
| columnDeclType | ✅ | `src/root.zig` |
|
|
| **Transacciones** | | |
|
|
| begin/commit/rollback | ✅ | `src/root.zig` |
|
|
| beginImmediate | ✅ | `src/root.zig` |
|
|
| beginExclusive | ✅ | `src/root.zig` |
|
|
| **Savepoints** | | |
|
|
| savepoint(name) | ✅ | `src/root.zig` |
|
|
| release(name) | ✅ | `src/root.zig` |
|
|
| rollbackTo(name) | ✅ | `src/root.zig` |
|
|
| **Pragmas/Config** | | |
|
|
| setBusyTimeout | ✅ | `src/root.zig` |
|
|
| setJournalMode | ✅ | `src/root.zig` |
|
|
| setSynchronous | ✅ | `src/root.zig` |
|
|
| enableWalMode | ✅ | `src/root.zig` |
|
|
| setForeignKeys | ✅ | `src/root.zig` |
|
|
| **ATTACH/DETACH** | | |
|
|
| attach(file, schema) | ✅ | `src/root.zig` |
|
|
| attachMemory(schema) | ✅ | `src/root.zig` |
|
|
| detach(schema) | ✅ | `src/root.zig` |
|
|
| listDatabases() | ✅ | `src/root.zig` |
|
|
| **Backup API** | | |
|
|
| Backup.init/initMain | ✅ | `src/root.zig` |
|
|
| Backup.step/stepAll | ✅ | `src/root.zig` |
|
|
| Backup.finish/deinit | ✅ | `src/root.zig` |
|
|
| Backup.remaining/pageCount | ✅ | `src/root.zig` |
|
|
| Backup.progress | ✅ | `src/root.zig` |
|
|
| backupDatabase() | ✅ | `src/root.zig` |
|
|
| backupToFile() | ✅ | `src/root.zig` |
|
|
| loadFromFile() | ✅ | `src/root.zig` |
|
|
| **User-Defined Functions** | | |
|
|
| createScalarFunction() | ✅ | `src/root.zig` |
|
|
| removeFunction() | ✅ | `src/root.zig` |
|
|
| FunctionContext (setInt/Float/Text/etc) | ✅ | `src/root.zig` |
|
|
| FunctionValue (asInt/Float/Text/etc) | ✅ | `src/root.zig` |
|
|
| **Custom Collations** | | |
|
|
| createCollation() | ✅ | `src/root.zig` |
|
|
| removeCollation() | ✅ | `src/root.zig` |
|
|
| **Aggregate Functions** | | |
|
|
| createAggregateFunction() | ✅ | `src/root.zig` |
|
|
| AggregateContext | ✅ | `src/root.zig` |
|
|
| **Blob I/O** | | |
|
|
| Blob.open/openAlloc | ✅ | `src/root.zig` |
|
|
| Blob.read/write | ✅ | `src/root.zig` |
|
|
| Blob.bytes | ✅ | `src/root.zig` |
|
|
| Blob.reopen | ✅ | `src/root.zig` |
|
|
| Blob.readAll | ✅ | `src/root.zig` |
|
|
| **Hooks** | | |
|
|
| setCommitHook | ✅ | `src/root.zig` |
|
|
| setRollbackHook | ✅ | `src/root.zig` |
|
|
| setUpdateHook | ✅ | `src/root.zig` |
|
|
| clearHooks | ✅ | `src/root.zig` |
|
|
| **Utilidades** | | |
|
|
| lastInsertRowId | ✅ | `src/root.zig` |
|
|
| changes/totalChanges | ✅ | `src/root.zig` |
|
|
| errorMessage | ✅ | `src/root.zig` |
|
|
| errorCode/extendedErrorCode | ✅ | `src/root.zig` |
|
|
| interrupt | ✅ | `src/root.zig` |
|
|
| isReadOnly | ✅ | `src/root.zig` |
|
|
| filename | ✅ | `src/root.zig` |
|
|
| version/versionNumber | ✅ | `src/root.zig` |
|
|
|
|
### Tests
|
|
|
|
| Categoria | Tests | Estado |
|
|
|-----------|-------|--------|
|
|
| Version | 1 | ✅ |
|
|
| Open/Close | 1 | ✅ |
|
|
| Create/Insert | 1 | ✅ |
|
|
| Prepared Statements | 1 | ✅ |
|
|
| Select Query | 1 | ✅ |
|
|
| Transaction Commit | 1 | ✅ |
|
|
| Transaction Rollback | 1 | ✅ |
|
|
| Foreign Keys | 1 | ✅ |
|
|
| Null Values | 1 | ✅ |
|
|
| Blob Data | 1 | ✅ |
|
|
| Named Parameters | 1 | ✅ |
|
|
| Savepoints | 1 | ✅ |
|
|
| Busy Timeout | 1 | ✅ |
|
|
| Statement Metadata | 1 | ✅ |
|
|
| Boolean bind/column | 1 | ✅ |
|
|
| Backup memory to memory | 1 | ✅ |
|
|
| Backup convenience | 1 | ✅ |
|
|
| ATTACH/DETACH | 1 | ✅ |
|
|
| User-defined functions | 1 | ✅ |
|
|
| Custom collations | 1 | ✅ |
|
|
| Blob I/O | 3 | ✅ |
|
|
| Hooks | 3 | ✅ |
|
|
| Aggregate functions | 2 | ✅ |
|
|
| **Total** | **28** | ✅ |
|
|
|
|
---
|
|
|
|
## Roadmap
|
|
|
|
### Fase 1 - Core (COMPLETADO)
|
|
- [x] Estructura proyecto
|
|
- [x] Compilar SQLite amalgamation
|
|
- [x] Abrir/cerrar bases de datos
|
|
- [x] Ejecutar SQL simple (exec)
|
|
- [x] Prepared statements basicos
|
|
- [x] Bind de parametros (int, text, blob, null, float)
|
|
- [x] Iterador de resultados
|
|
- [x] Transacciones basicas
|
|
|
|
### Fase 2A - Paridad CGo Core (COMPLETADO)
|
|
- [x] SAVEPOINT support
|
|
- [x] Named parameters (:name, @name, $name)
|
|
- [x] Busy timeout
|
|
- [x] WAL mode helpers
|
|
- [x] Statement metadata (sql, isReadOnly, parameterCount)
|
|
- [x] Boolean bind/column
|
|
- [x] Error codes (errorCode, extendedErrorCode)
|
|
- [x] Database info (isReadOnly, filename)
|
|
- [x] interrupt()
|
|
|
|
### Fase 2B - Paridad CGo Avanzada (COMPLETADO)
|
|
- [x] Backup API completo
|
|
- [x] User-defined functions (scalar)
|
|
- [x] Collations personalizadas
|
|
- [x] ATTACH/DETACH databases
|
|
- [ ] Batch bind con tuples/structs
|
|
- [ ] Row iterator idiomatico
|
|
|
|
### Fase 3A - Avanzado (COMPLETADO)
|
|
- [x] Blob streaming (para archivos grandes)
|
|
- [x] User-defined functions (aggregate)
|
|
- [x] Update/Commit/Rollback hooks
|
|
|
|
### Fase 3B - Avanzado (EN PROGRESO)
|
|
- [ ] Authorizer callback
|
|
- [ ] Progress handler
|
|
- [ ] Pre-update hook
|
|
- [ ] Window functions
|
|
- [ ] Busy handler (custom callback)
|
|
- [ ] Batch bind con tuples/structs
|
|
- [ ] Row iterator idiomatico
|
|
- [ ] Connection pooling
|
|
|
|
### Fase 4 - Extras
|
|
- [ ] Connection URI parsing
|
|
- [ ] Virtual tables
|
|
- [ ] FTS5 helpers
|
|
- [ ] JSON1 helpers
|
|
- [ ] R-Tree helpers
|
|
|
|
---
|
|
|
|
## Arquitectura
|
|
|
|
### Estructura de Archivos
|
|
|
|
```
|
|
zsqlite/
|
|
├── CLAUDE.md # Este archivo - estado del proyecto
|
|
├── build.zig # Sistema de build
|
|
├── src/
|
|
│ └── root.zig # Wrapper principal (~1000 lineas)
|
|
├── vendor/
|
|
│ ├── sqlite3.c # SQLite 3.47.2 amalgamation
|
|
│ ├── sqlite3.h # Headers SQLite
|
|
│ ├── sqlite3ext.h # Extension headers
|
|
│ └── shell.c # SQLite shell (no usado)
|
|
├── examples/
|
|
│ └── basic.zig # Ejemplo basico funcional
|
|
└── docs/
|
|
├── ARCHITECTURE.md # Diseno interno
|
|
├── API.md # Referencia rapida API
|
|
└── CGO_PARITY_ANALYSIS.md # Analisis de paridad con go-sqlite3
|
|
```
|
|
|
|
### SQLite Amalgamation
|
|
|
|
**Version**: SQLite 3.47.2
|
|
|
|
**Flags de compilacion**:
|
|
```
|
|
-DSQLITE_DQS=0 # Disable double-quoted strings
|
|
-DSQLITE_THREADSAFE=0 # Single-threaded (mas rapido)
|
|
-DSQLITE_DEFAULT_MEMSTATUS=0 # Disable memory tracking
|
|
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
|
|
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS
|
|
-DSQLITE_OMIT_DEPRECATED
|
|
-DSQLITE_OMIT_SHARED_CACHE
|
|
-DSQLITE_ENABLE_FTS5 # Full-text search
|
|
-DSQLITE_ENABLE_JSON1 # JSON functions
|
|
-DSQLITE_ENABLE_RTREE # R-Tree geospatial
|
|
-DSQLITE_OMIT_LOAD_EXTENSION # No dynamic extensions
|
|
```
|
|
|
|
---
|
|
|
|
## API Actual
|
|
|
|
### Abrir Base de Datos
|
|
|
|
```zig
|
|
const sqlite = @import("zsqlite");
|
|
|
|
// In-memory
|
|
var db = try sqlite.openMemory();
|
|
defer db.close();
|
|
|
|
// Archivo
|
|
var db = try sqlite.open("test.db");
|
|
defer db.close();
|
|
|
|
// Con flags
|
|
var db = try sqlite.Database.openWithFlags("test.db", .{
|
|
.read_only = true,
|
|
.create = false,
|
|
});
|
|
defer db.close();
|
|
```
|
|
|
|
### Ejecutar SQL Simple
|
|
|
|
```zig
|
|
try db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
|
|
try db.exec("INSERT INTO users (name) VALUES ('Alice')");
|
|
|
|
// Con string runtime
|
|
try db.execAlloc(allocator, sql_string);
|
|
```
|
|
|
|
### Prepared Statements
|
|
|
|
```zig
|
|
var stmt = try db.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
|
|
defer stmt.finalize();
|
|
|
|
try stmt.bindText(1, "Bob");
|
|
try stmt.bindInt(2, 30);
|
|
_ = try stmt.step();
|
|
|
|
// Reusar
|
|
try stmt.reset();
|
|
try stmt.clearBindings();
|
|
try stmt.bindText(1, "Charlie");
|
|
try stmt.bindInt(2, 25);
|
|
_ = try stmt.step();
|
|
```
|
|
|
|
### Named Parameters (NUEVO v0.2)
|
|
|
|
```zig
|
|
var stmt = try db.prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
|
|
defer stmt.finalize();
|
|
|
|
try stmt.bindTextNamed(":name", "Alice");
|
|
try stmt.bindIntNamed(":age", 30);
|
|
_ = try stmt.step();
|
|
|
|
// Tambien soporta @name y $name estilos
|
|
```
|
|
|
|
### Queries
|
|
|
|
```zig
|
|
var stmt = try db.prepare("SELECT id, name FROM users ORDER BY id");
|
|
defer stmt.finalize();
|
|
|
|
while (try stmt.step()) {
|
|
const id = stmt.columnInt(0);
|
|
const name = stmt.columnText(1) orelse "(null)";
|
|
std.debug.print("User {}: {s}\n", .{ id, name });
|
|
}
|
|
```
|
|
|
|
### Transacciones
|
|
|
|
```zig
|
|
try db.begin();
|
|
errdefer db.rollback() catch {};
|
|
|
|
try db.exec("INSERT INTO users (name) VALUES ('Alice')");
|
|
try db.exec("INSERT INTO users (name) VALUES ('Bob')");
|
|
|
|
try db.commit();
|
|
```
|
|
|
|
### Savepoints (NUEVO v0.2)
|
|
|
|
```zig
|
|
const allocator = std.heap.page_allocator;
|
|
|
|
try db.begin();
|
|
try db.savepoint(allocator, "sp1");
|
|
|
|
try db.exec("INSERT INTO test VALUES (1)");
|
|
|
|
// Revertir al savepoint
|
|
try db.rollbackTo(allocator, "sp1");
|
|
|
|
// O confirmar el savepoint
|
|
try db.release(allocator, "sp1");
|
|
|
|
try db.commit();
|
|
```
|
|
|
|
### WAL Mode (NUEVO v0.2)
|
|
|
|
```zig
|
|
const allocator = std.heap.page_allocator;
|
|
|
|
// Habilitar WAL mode con settings recomendados
|
|
try db.enableWalMode(allocator);
|
|
|
|
// O configurar individualmente
|
|
try db.setJournalMode(allocator, "WAL");
|
|
try db.setSynchronous(allocator, "NORMAL");
|
|
try db.setBusyTimeout(5000); // 5 segundos
|
|
```
|
|
|
|
### Foreign Keys
|
|
|
|
```zig
|
|
try db.setForeignKeys(true);
|
|
```
|
|
|
|
---
|
|
|
|
## Comandos
|
|
|
|
```bash
|
|
# Zig path
|
|
ZIG=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2/zig
|
|
|
|
# Compilar
|
|
$ZIG build
|
|
|
|
# Tests
|
|
$ZIG build test
|
|
|
|
# Ejecutar ejemplo
|
|
$ZIG build basic && ./zig-out/bin/basic
|
|
```
|
|
|
|
---
|
|
|
|
## Equipo y Metodologia
|
|
|
|
### Normas de Trabajo Centralizadas
|
|
|
|
**IMPORTANTE**: Todas las normas de trabajo estan en:
|
|
```
|
|
/mnt/cello2/arno/re/recode/TEAM_STANDARDS/
|
|
```
|
|
|
|
**Archivos clave a leer**:
|
|
- `LAST_UPDATE.md` - **LEER PRIMERO** - Cambios recientes en normas
|
|
- `NORMAS_TRABAJO_CONSENSUADAS.md` - Metodologia fundamental
|
|
- `QUICK_REFERENCE.md` - Cheat sheet rapido
|
|
|
|
### Estandares Zig Open Source (Seccion #24)
|
|
|
|
- **Claridad**: Codigo autoexplicativo, nombres descriptivos
|
|
- **Doc comments**: `///` en todas las funciones publicas
|
|
- **Idiomatico**: snake_case, error handling explicito
|
|
- **Sin magia**: Preferir codigo explicito sobre abstracciones complejas
|
|
|
|
### Control de Versiones
|
|
|
|
```bash
|
|
# Remote
|
|
git remote: git@git.reugenio.com:reugenio/zsqlite.git
|
|
|
|
# Branches
|
|
main # Codigo estable
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
### CGo go-sqlite3 (Referencia principal)
|
|
- Repo: https://github.com/mattn/go-sqlite3
|
|
- Objetivo: Replicar toda su funcionalidad en Zig
|
|
- Analisis: `docs/CGO_PARITY_ANALYSIS.md`
|
|
|
|
### SQLite C API
|
|
- Docs: https://sqlite.org/c3ref/intro.html
|
|
- Objetivo: Wrapper completo de funciones relevantes
|
|
|
|
### Otros Wrappers Zig (Referencia)
|
|
- zig-sqlite: https://github.com/vrischmann/zig-sqlite
|
|
- zqlite.zig: https://github.com/karlseguin/zqlite.zig
|
|
|
|
---
|
|
|
|
## Historial de Desarrollo
|
|
|
|
### 2025-12-08 - v0.3 (Fase 2B Completada)
|
|
- **Backup API completo**:
|
|
- Backup struct con init/step/finish/remaining/pageCount/progress
|
|
- Funciones de conveniencia: backupDatabase, backupToFile, loadFromFile
|
|
- **User-Defined Functions (Scalar)**:
|
|
- createScalarFunction() para registrar funciones Zig en SQL
|
|
- FunctionContext para retornar resultados
|
|
- FunctionValue para acceder a argumentos
|
|
- Cleanup automatico con destructores
|
|
- **Custom Collations**:
|
|
- createCollation() para orden personalizado de strings
|
|
- removeCollation() para eliminar collations
|
|
- **ATTACH/DETACH Databases**:
|
|
- attach(), attachMemory(), detach()
|
|
- listDatabases() para enumerar schemas adjuntos
|
|
- 20 tests pasando
|
|
- ~1700 lineas de codigo en root.zig
|
|
|
|
### 2025-12-08 - v0.2 (Fase 2A Completada)
|
|
- Named parameters (:name, @name, $name)
|
|
- Savepoints (savepoint, release, rollbackTo)
|
|
- WAL mode helpers (enableWalMode, setJournalMode, setSynchronous)
|
|
- Busy timeout (setBusyTimeout)
|
|
- Statement metadata (sql, isReadOnly, parameterCount, parameterIndex, parameterName)
|
|
- Boolean bind/column (bindBool, columnBool)
|
|
- Error codes (errorCode, extendedErrorCode)
|
|
- Database info (isReadOnly, filename, interrupt)
|
|
- bindZeroblob, columnBytes, columnDeclType
|
|
- 15 tests pasando
|
|
- Documentacion docs/ creada (ARCHITECTURE.md, API.md, CGO_PARITY_ANALYSIS.md)
|
|
|
|
### 2025-12-08 - v0.1 (Core Funcional)
|
|
- Estructura inicial del proyecto
|
|
- SQLite 3.47.2 amalgamation compilando
|
|
- Database, Statement, Error types
|
|
- Bind parameters completos (null, int, float, text, blob)
|
|
- Column access completo
|
|
- Transacciones basicas
|
|
- 10 tests unitarios pasando
|
|
- Ejemplo basic.zig funcional
|
|
|
|
---
|
|
|
|
## Notas de Desarrollo
|
|
|
|
### Proxima Sesion
|
|
1. Blob streaming (para archivos grandes)
|
|
2. User-defined aggregate functions
|
|
3. Update/Commit hooks
|
|
4. Batch bind con comptime structs
|
|
|
|
### Lecciones Aprendidas
|
|
- SQLite amalgamation compila limpiamente con Zig build system
|
|
- `@cImport` funciona bien para headers SQLite
|
|
- SQLITE_TRANSIENT necesario para strings/blobs que Zig puede mover
|
|
- En Zig 0.15 usar `std.fmt.allocPrint` + `\x00` manual en lugar de `allocPrintZ`
|
|
- Named parameters funcionan con el prefijo incluido (":name", no "name")
|
|
- Callbacks C requieren `callconv(.c)` y estructuras wrapper para pasar estado
|
|
- `std.ArrayListUnmanaged` en Zig 0.15 (no `ArrayList.init`)
|
|
- UDFs y Collations usan page_allocator interno para simplicidad
|
|
|
|
---
|
|
|
|
**© zsqlite - Wrapper SQLite para Zig**
|
|
*2025-12-08 - En desarrollo activo*
|