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>
This commit is contained in:
parent
a290859182
commit
532cf827f8
5 changed files with 2817 additions and 132 deletions
579
CLAUDE.md
579
CLAUDE.md
|
|
@ -1,165 +1,406 @@
|
||||||
# zsqlite - SQLite Wrapper para Zig
|
# zsqlite - SQLite Wrapper para Zig
|
||||||
|
|
||||||
> **Fecha creación**: 2025-12-08
|
> **Ultima actualizacion**: 2025-12-08
|
||||||
> **Versión Zig**: 0.15.2
|
> **Lenguaje**: Zig 0.15.2
|
||||||
> **Estado**: En desarrollo inicial
|
> **Estado**: v0.3 - Fase 2B completada
|
||||||
|
> **Inspiracion**: CGo go-sqlite3, SQLite C API
|
||||||
|
|
||||||
## Descripción del Proyecto
|
## Descripcion del Proyecto
|
||||||
|
|
||||||
Wrapper idiomático de SQLite para Zig. Compila SQLite amalgamation directamente en el binario, resultando en un ejecutable único sin dependencias externas.
|
**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.
|
||||||
|
|
||||||
**Filosofía**:
|
**Filosofia**:
|
||||||
- Zero dependencias runtime
|
- Zero dependencias runtime
|
||||||
- API idiomática Zig (errores, allocators, iteradores)
|
- API idiomatica Zig (errores, allocators, iteradores)
|
||||||
- Binario único y portable
|
- Binario unico y portable
|
||||||
- Compatible con bases de datos SQLite existentes
|
- 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.3 (Fase 2B 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` |
|
||||||
|
| **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 | ✅ |
|
||||||
|
| **Total** | **20** | ✅ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 3 - Avanzado (EN PROGRESO)
|
||||||
|
- [ ] Blob streaming (para archivos grandes)
|
||||||
|
- [ ] User-defined functions (aggregate)
|
||||||
|
- [ ] Authorizer callback
|
||||||
|
- [ ] Progress handler
|
||||||
|
- [ ] Update/Commit hooks
|
||||||
|
- [ ] Connection pooling
|
||||||
|
|
||||||
|
### Fase 4 - Extras
|
||||||
|
- [ ] Connection URI parsing
|
||||||
|
- [ ] Virtual tables
|
||||||
|
- [ ] FTS5 helpers
|
||||||
|
- [ ] JSON1 helpers
|
||||||
|
- [ ] R-Tree helpers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Arquitectura
|
## Arquitectura
|
||||||
|
|
||||||
|
### Estructura de Archivos
|
||||||
|
|
||||||
```
|
```
|
||||||
zsqlite/
|
zsqlite/
|
||||||
├── CLAUDE.md # Este archivo
|
├── CLAUDE.md # Este archivo - estado del proyecto
|
||||||
├── build.zig # Sistema de build
|
├── build.zig # Sistema de build
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── root.zig # Exports públicos
|
│ └── root.zig # Wrapper principal (~1000 lineas)
|
||||||
│ ├── sqlite.zig # Wrapper principal
|
|
||||||
│ ├── statement.zig # Prepared statements
|
|
||||||
│ ├── row.zig # Iterador de filas
|
|
||||||
│ └── errors.zig # Mapeo de errores SQLite
|
|
||||||
├── vendor/
|
├── vendor/
|
||||||
│ ├── sqlite3.c # SQLite amalgamation
|
│ ├── sqlite3.c # SQLite 3.47.2 amalgamation
|
||||||
│ └── sqlite3.h # Headers SQLite
|
│ ├── sqlite3.h # Headers SQLite
|
||||||
└── examples/
|
│ ├── sqlite3ext.h # Extension headers
|
||||||
└── basic.zig # Ejemplo básico
|
│ └── 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
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Objetivo
|
### SQLite Amalgamation
|
||||||
|
|
||||||
```zig
|
**Version**: SQLite 3.47.2
|
||||||
const std = @import("std");
|
|
||||||
const sqlite = @import("zsqlite");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
**Flags de compilacion**:
|
||||||
var db = try sqlite.open("test.db");
|
|
||||||
defer db.close();
|
|
||||||
|
|
||||||
// Ejecutar SQL directo
|
|
||||||
try db.exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)");
|
|
||||||
|
|
||||||
// Prepared statement con parámetros
|
|
||||||
var stmt = try db.prepare("INSERT INTO users (name) VALUES (?)");
|
|
||||||
defer stmt.deinit();
|
|
||||||
|
|
||||||
try stmt.bind(.{ "Alice" });
|
|
||||||
try stmt.exec();
|
|
||||||
|
|
||||||
stmt.reset();
|
|
||||||
try stmt.bind(.{ "Bob" });
|
|
||||||
try stmt.exec();
|
|
||||||
|
|
||||||
// Query con iterador
|
|
||||||
var query = try db.prepare("SELECT id, name FROM users");
|
|
||||||
defer query.deinit();
|
|
||||||
|
|
||||||
while (try query.step()) |row| {
|
|
||||||
const id = row.int(0);
|
|
||||||
const name = row.text(1);
|
|
||||||
std.debug.print("User {}: {s}\n", .{ id, name });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Funcionalidades Planificadas
|
|
||||||
|
|
||||||
### Fase 1 - Core (Actual)
|
|
||||||
- [x] Estructura proyecto
|
|
||||||
- [ ] Compilar SQLite amalgamation
|
|
||||||
- [ ] Abrir/cerrar bases de datos
|
|
||||||
- [ ] Ejecutar SQL simple (exec)
|
|
||||||
- [ ] Prepared statements básicos
|
|
||||||
- [ ] Bind de parámetros (int, text, blob, null)
|
|
||||||
- [ ] Iterador de resultados
|
|
||||||
|
|
||||||
### Fase 2 - Transacciones y Errores
|
|
||||||
- [ ] BEGIN/COMMIT/ROLLBACK helpers
|
|
||||||
- [ ] Mapeo completo errores SQLite
|
|
||||||
- [ ] SAVEPOINT support
|
|
||||||
|
|
||||||
### Fase 3 - Avanzado
|
|
||||||
- [ ] Blob streaming (para archivos grandes)
|
|
||||||
- [ ] User-defined functions
|
|
||||||
- [ ] Collations personalizadas
|
|
||||||
- [ ] Backup API
|
|
||||||
|
|
||||||
## SQLite Amalgamation
|
|
||||||
|
|
||||||
Usamos SQLite amalgamation (sqlite3.c + sqlite3.h) que es la forma recomendada de embeber SQLite. Es un único archivo .c con todo el código de SQLite.
|
|
||||||
|
|
||||||
**Versión objetivo**: SQLite 3.45+ (última estable)
|
|
||||||
|
|
||||||
**Flags de compilación recomendados**:
|
|
||||||
```
|
```
|
||||||
-DSQLITE_DQS=0 # Disable double-quoted strings
|
-DSQLITE_DQS=0 # Disable double-quoted strings
|
||||||
-DSQLITE_THREADSAFE=0 # Single-threaded (más rápido)
|
-DSQLITE_THREADSAFE=0 # Single-threaded (mas rapido)
|
||||||
-DSQLITE_DEFAULT_MEMSTATUS=0 # Disable memory tracking
|
-DSQLITE_DEFAULT_MEMSTATUS=0 # Disable memory tracking
|
||||||
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
|
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
|
||||||
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS
|
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS
|
||||||
-DSQLITE_OMIT_DEPRECATED
|
-DSQLITE_OMIT_DEPRECATED
|
||||||
-DSQLITE_OMIT_SHARED_CACHE
|
-DSQLITE_OMIT_SHARED_CACHE
|
||||||
-DSQLITE_USE_ALLOCA
|
|
||||||
-DSQLITE_ENABLE_FTS5 # Full-text search
|
-DSQLITE_ENABLE_FTS5 # Full-text search
|
||||||
-DSQLITE_ENABLE_JSON1 # JSON functions
|
-DSQLITE_ENABLE_JSON1 # JSON functions
|
||||||
|
-DSQLITE_ENABLE_RTREE # R-Tree geospatial
|
||||||
|
-DSQLITE_OMIT_LOAD_EXTENSION # No dynamic extensions
|
||||||
```
|
```
|
||||||
|
|
||||||
## Compilación
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Descargar SQLite amalgamation (una sola vez)
|
|
||||||
cd vendor
|
|
||||||
curl -O https://sqlite.org/2024/sqlite-amalgamation-3450000.zip
|
|
||||||
unzip sqlite-amalgamation-3450000.zip
|
|
||||||
mv sqlite-amalgamation-3450000/* .
|
|
||||||
rm -rf sqlite-amalgamation-3450000*
|
|
||||||
|
|
||||||
# Compilar
|
|
||||||
zig build
|
|
||||||
|
|
||||||
# Ejecutar tests
|
|
||||||
zig build test
|
|
||||||
|
|
||||||
# Ejecutar ejemplo
|
|
||||||
zig build example
|
|
||||||
```
|
|
||||||
|
|
||||||
## Diferencias con otros wrappers
|
|
||||||
|
|
||||||
| Característica | zsqlite | zig-sqlite | zqlite.zig |
|
|
||||||
|----------------|---------|------------|------------|
|
|
||||||
| Compila SQLite | Sí | No (linkea) | Sí |
|
|
||||||
| API idiomática | Sí | Parcial | Sí |
|
|
||||||
| Zero alloc option | Planificado | No | No |
|
|
||||||
| Zig 0.15 | Sí | ? | ? |
|
|
||||||
|
|
||||||
## Uso en simifactu-zig (futuro)
|
|
||||||
|
|
||||||
Este wrapper está diseñado para ser drop-in replacement del uso de SQLite en simifactu-fyne, soportando:
|
|
||||||
- Foreign keys
|
|
||||||
- Transactions
|
|
||||||
- Prepared statements
|
|
||||||
- BLOB storage
|
|
||||||
- JSON functions
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Equipo y Metodología
|
## API Actual
|
||||||
|
|
||||||
### Normas de Trabajo
|
### Abrir Base de Datos
|
||||||
|
|
||||||
**IMPORTANTE**: Todas las normas de trabajo están en:
|
```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/
|
/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
|
### Control de Versiones
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -167,18 +408,92 @@ Este wrapper está diseñado para ser drop-in replacement del uso de SQLite en s
|
||||||
git remote: git@git.reugenio.com:reugenio/zsqlite.git
|
git remote: git@git.reugenio.com:reugenio/zsqlite.git
|
||||||
|
|
||||||
# Branches
|
# Branches
|
||||||
main # Código estable
|
main # Codigo estable
|
||||||
develop # Desarrollo activo
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Zig Path
|
---
|
||||||
|
|
||||||
```bash
|
## Referencias
|
||||||
ZIG=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2/zig
|
|
||||||
```
|
### 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
|
## Notas de Desarrollo
|
||||||
|
|
||||||
*Se irán añadiendo conforme avance el proyecto*
|
### 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*
|
||||||
|
|
|
||||||
533
docs/API.md
Normal file
533
docs/API.md
Normal file
|
|
@ -0,0 +1,533 @@
|
||||||
|
# zsqlite - API Reference
|
||||||
|
|
||||||
|
> **Version**: 0.3
|
||||||
|
> **Ultima actualizacion**: 2025-12-08
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Custom collations
|
||||||
|
try db.createCollation("NOCASE2", myCaseInsensitiveCompare);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Funciones de Modulo
|
||||||
|
|
||||||
|
### open
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn openMemory() Error!Database
|
||||||
|
```
|
||||||
|
|
||||||
|
Abre una base de datos en memoria.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### version / versionNumber
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn version() []const u8
|
||||||
|
pub fn versionNumber() i32
|
||||||
|
```
|
||||||
|
|
||||||
|
Retorna la version de SQLite como string ("3.47.2") o numero (3047002).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### backupDatabase
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn backupDatabase(dest_db: *Database, source_db: *Database) Error!void
|
||||||
|
```
|
||||||
|
|
||||||
|
Copia una base de datos completa a otra.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### backupToFile
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn backupToFile(source_db: *Database, path: [:0]const u8) Error!void
|
||||||
|
```
|
||||||
|
|
||||||
|
Guarda una base de datos a un archivo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### loadFromFile
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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:**
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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:**
|
||||||
|
```zig
|
||||||
|
try db.attachMemory(alloc, "cache");
|
||||||
|
try db.exec("CREATE TABLE cache.items (...)");
|
||||||
|
// SELECT * FROM cache.items
|
||||||
|
try db.detach(alloc, "cache");
|
||||||
|
```
|
||||||
|
|
||||||
|
### User-Defined Functions
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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:**
|
||||||
|
```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(value) FROM table
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Collations
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub fn createCollation(self: *Database, name: [:0]const u8, func: CollationFn) !void
|
||||||
|
pub fn removeCollation(self: *Database, name: [:0]const u8) Error!void
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ejemplo:**
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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:**
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const ScalarFn = *const fn (ctx: FunctionContext, args: []const FunctionValue) void;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
### OpenFlags
|
||||||
|
|
||||||
|
```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,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### ColumnType
|
||||||
|
|
||||||
|
```zig
|
||||||
|
pub const ColumnType = enum {
|
||||||
|
integer,
|
||||||
|
float,
|
||||||
|
text,
|
||||||
|
blob,
|
||||||
|
null_value,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### CollationFn
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
|
||||||
|
```zig
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**© zsqlite v0.3 - API Reference**
|
||||||
|
*2025-12-08*
|
||||||
397
docs/ARCHITECTURE.md
Normal file
397
docs/ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,397 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
```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. 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`
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
### 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*
|
||||||
334
docs/CGO_PARITY_ANALYSIS.md
Normal file
334
docs/CGO_PARITY_ANALYSIS.md
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
# Analisis de Paridad con CGo go-sqlite3
|
||||||
|
|
||||||
|
> **Fecha**: 2025-12-08
|
||||||
|
> **Objetivo**: Identificar todas las funcionalidades de go-sqlite3 para replicarlas en zsqlite
|
||||||
|
|
||||||
|
## Resumen
|
||||||
|
|
||||||
|
go-sqlite3 (https://github.com/mattn/go-sqlite3) es el wrapper SQLite mas maduro para Go.
|
||||||
|
Este documento analiza todas sus funcionalidades para asegurar paridad en zsqlite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estado de Implementacion
|
||||||
|
|
||||||
|
### Leyenda
|
||||||
|
- ✅ Implementado en zsqlite
|
||||||
|
- ⏳ Pendiente de implementar
|
||||||
|
- 🔄 Parcialmente implementado
|
||||||
|
- ❌ No aplicable a Zig
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Conexion a Base de Datos
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|---------------|------------|---------|-------|
|
||||||
|
| Open basico | ✅ | ✅ | `sqlite.open()` |
|
||||||
|
| Open con flags | ✅ | ✅ | `Database.openWithFlags()` |
|
||||||
|
| Close | ✅ | ✅ | `db.close()` |
|
||||||
|
| URI connection string | ✅ | ⏳ | Parsear `file:path?mode=ro&cache=shared` |
|
||||||
|
| DSN parameters | ✅ | ⏳ | Configuracion via string |
|
||||||
|
| Connection pooling | ✅ (via database/sql) | ⏳ | Pool propio |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Configuracion de Pragmas
|
||||||
|
|
||||||
|
| Pragma | go-sqlite3 | zsqlite | Prioridad |
|
||||||
|
|--------|------------|---------|-----------|
|
||||||
|
| auto_vacuum | ✅ | ⏳ | Media |
|
||||||
|
| busy_timeout | ✅ | ✅ | `db.setBusyTimeout()` |
|
||||||
|
| cache_size | ✅ | ⏳ | Media |
|
||||||
|
| case_sensitive_like | ✅ | ⏳ | Baja |
|
||||||
|
| defer_foreign_keys | ✅ | ⏳ | Media |
|
||||||
|
| foreign_keys | ✅ | ✅ | `db.setForeignKeys()` |
|
||||||
|
| journal_mode | ✅ | ✅ | `db.setJournalMode()` |
|
||||||
|
| locking_mode | ✅ | ⏳ | Media |
|
||||||
|
| query_only | ✅ | ⏳ | Baja |
|
||||||
|
| recursive_triggers | ✅ | ⏳ | Baja |
|
||||||
|
| secure_delete | ✅ | ⏳ | Baja |
|
||||||
|
| synchronous | ✅ | ✅ | `db.setSynchronous()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Prepared Statements
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|---------------|------------|---------|-------|
|
||||||
|
| Prepare | ✅ | ✅ | `db.prepare()` |
|
||||||
|
| Exec (sin resultados) | ✅ | ✅ | `db.exec()` |
|
||||||
|
| Query (con resultados) | ✅ | ✅ | `stmt.step()` loop |
|
||||||
|
| Bind posicional (?) | ✅ | ✅ | `stmt.bindInt(1, val)` |
|
||||||
|
| Bind named (:name) | ✅ | ✅ | `stmt.bindIntNamed(":name", val)` |
|
||||||
|
| Bind named (@name) | ✅ | ✅ | `stmt.bindIntNamed("@name", val)` |
|
||||||
|
| Bind named ($name) | ✅ | ✅ | `stmt.bindIntNamed("$name", val)` |
|
||||||
|
| Readonly check | ✅ | ✅ | `stmt.isReadOnly()` |
|
||||||
|
| SQL text | ✅ | ✅ | `stmt.sql()` |
|
||||||
|
| Expanded SQL | ✅ | ⏳ | `sqlite3_expanded_sql()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Bind de Parametros
|
||||||
|
|
||||||
|
| Tipo | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|------|------------|---------|-------|
|
||||||
|
| NULL | ✅ | ✅ | `stmt.bindNull()` |
|
||||||
|
| int64 | ✅ | ✅ | `stmt.bindInt()` |
|
||||||
|
| float64 | ✅ | ✅ | `stmt.bindFloat()` |
|
||||||
|
| string/text | ✅ | ✅ | `stmt.bindText()` |
|
||||||
|
| []byte/blob | ✅ | ✅ | `stmt.bindBlob()` |
|
||||||
|
| bool | ✅ | ✅ | `stmt.bindBool()` |
|
||||||
|
| time.Time | ✅ | ⏳ | Formatear como string ISO8601 |
|
||||||
|
| Zeroblob | ✅ | ✅ | `stmt.bindZeroblob()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Lectura de Columnas
|
||||||
|
|
||||||
|
| Tipo | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|------|------------|---------|-------|
|
||||||
|
| Column count | ✅ | ✅ | `stmt.columnCount()` |
|
||||||
|
| Column name | ✅ | ✅ | `stmt.columnName()` |
|
||||||
|
| Column type | ✅ | ✅ | `stmt.columnType()` |
|
||||||
|
| int64 | ✅ | ✅ | `stmt.columnInt()` |
|
||||||
|
| float64 | ✅ | ✅ | `stmt.columnFloat()` |
|
||||||
|
| text | ✅ | ✅ | `stmt.columnText()` |
|
||||||
|
| blob | ✅ | ✅ | `stmt.columnBlob()` |
|
||||||
|
| NULL check | ✅ | ✅ | `stmt.columnIsNull()` |
|
||||||
|
| Column bytes | ✅ | ✅ | `stmt.columnBytes()` |
|
||||||
|
| Declared type | ✅ | ✅ | `stmt.columnDeclType()` |
|
||||||
|
| Database name | ✅ | ⏳ | `sqlite3_column_database_name()` |
|
||||||
|
| Table name | ✅ | ⏳ | `sqlite3_column_table_name()` |
|
||||||
|
| Origin name | ✅ | ⏳ | `sqlite3_column_origin_name()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Transacciones
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|---------------|------------|---------|-------|
|
||||||
|
| BEGIN | ✅ | ✅ | `db.begin()` |
|
||||||
|
| BEGIN IMMEDIATE | ✅ | ✅ | `db.beginImmediate()` |
|
||||||
|
| BEGIN EXCLUSIVE | ✅ | ✅ | `db.beginExclusive()` |
|
||||||
|
| BEGIN DEFERRED | ✅ | ✅ | `db.begin()` (default) |
|
||||||
|
| COMMIT | ✅ | ✅ | `db.commit()` |
|
||||||
|
| ROLLBACK | ✅ | ✅ | `db.rollback()` |
|
||||||
|
| SAVEPOINT | ✅ | ✅ | `db.savepoint(alloc, "name")` |
|
||||||
|
| RELEASE | ✅ | ✅ | `db.release(alloc, "name")` |
|
||||||
|
| ROLLBACK TO | ✅ | ✅ | `db.rollbackTo(alloc, "name")` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Metadatos y Utilidades
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|---------------|------------|---------|-------|
|
||||||
|
| LastInsertRowId | ✅ | ✅ | `db.lastInsertRowId()` |
|
||||||
|
| Changes | ✅ | ✅ | `db.changes()` |
|
||||||
|
| TotalChanges | ✅ | ✅ | `db.totalChanges()` |
|
||||||
|
| Error message | ✅ | ✅ | `db.errorMessage()` |
|
||||||
|
| Error code | ✅ | ✅ | `db.errorCode()` |
|
||||||
|
| Extended error code | ✅ | ✅ | `db.extendedErrorCode()` |
|
||||||
|
| SQLite version | ✅ | ✅ | `sqlite.version()` |
|
||||||
|
| Version number | ✅ | ✅ | `sqlite.versionNumber()` |
|
||||||
|
| Database filename | ✅ | ✅ | `db.filename()` |
|
||||||
|
| Is readonly | ✅ | ✅ | `db.isReadOnly()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Limites y Control
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Prioridad |
|
||||||
|
|---------------|------------|---------|-----------|
|
||||||
|
| GetLimit | ✅ | ⏳ | Baja |
|
||||||
|
| SetLimit | ✅ | ⏳ | Baja |
|
||||||
|
| SetFileControlInt | ✅ | ⏳ | Baja |
|
||||||
|
| Interrupt | ✅ | ✅ | `db.interrupt()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Callbacks y Hooks
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Prioridad |
|
||||||
|
|---------------|------------|---------|-----------|
|
||||||
|
| Commit hook | ✅ | ⏳ | Media |
|
||||||
|
| Rollback hook | ✅ | ⏳ | Media |
|
||||||
|
| Update hook | ✅ | ⏳ | Media |
|
||||||
|
| Pre-update hook | ✅ | ⏳ | Baja |
|
||||||
|
| Authorizer | ✅ | ⏳ | Baja |
|
||||||
|
| Progress handler | ✅ | ⏳ | Baja |
|
||||||
|
| Busy handler | ✅ | ⏳ | Media |
|
||||||
|
| Busy timeout | ✅ | ✅ | `db.setBusyTimeout()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Funciones Personalizadas
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Prioridad |
|
||||||
|
|---------------|------------|---------|-----------|
|
||||||
|
| RegisterFunc (scalar) | ✅ | ✅ | `db.createScalarFunction()` |
|
||||||
|
| RegisterAggregator | ✅ | ⏳ | Media |
|
||||||
|
| RegisterCollation | ✅ | ✅ | `db.createCollation()` |
|
||||||
|
| User-defined window func | ✅ | ⏳ | Baja |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Backup API
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Prioridad |
|
||||||
|
|---------------|------------|---------|-----------|
|
||||||
|
| Backup init | ✅ | ✅ | `Backup.init()` |
|
||||||
|
| Backup step | ✅ | ✅ | `backup.step()` |
|
||||||
|
| Backup finish | ✅ | ✅ | `backup.finish()` |
|
||||||
|
| Backup remaining | ✅ | ✅ | `backup.remaining()` |
|
||||||
|
| Backup pagecount | ✅ | ✅ | `backup.pageCount()` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Blob I/O
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Prioridad |
|
||||||
|
|---------------|------------|---------|-----------|
|
||||||
|
| Blob open | ✅ | ⏳ | Media |
|
||||||
|
| Blob close | ✅ | ⏳ | Media |
|
||||||
|
| Blob read | ✅ | ⏳ | Media |
|
||||||
|
| Blob write | ✅ | ⏳ | Media |
|
||||||
|
| Blob bytes | ✅ | ⏳ | Media |
|
||||||
|
| Blob reopen | ✅ | ⏳ | Baja |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Extensiones
|
||||||
|
|
||||||
|
| Funcionalidad | go-sqlite3 | zsqlite | Notas |
|
||||||
|
|---------------|------------|---------|-------|
|
||||||
|
| Load extension | ✅ | ❌ | Deshabilitado por seguridad |
|
||||||
|
| Enable load ext | ✅ | ❌ | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plan de Implementacion
|
||||||
|
|
||||||
|
### Fase 2A - Prioridad Alta ✅ COMPLETADA
|
||||||
|
1. ✅ Busy timeout/handler - `db.setBusyTimeout()`
|
||||||
|
2. ✅ WAL mode (journal_mode pragma) - `db.setJournalMode()`, `db.enableWalMode()`
|
||||||
|
3. ✅ Named parameters - `stmt.bindTextNamed()`, etc.
|
||||||
|
4. ✅ SAVEPOINT/RELEASE/ROLLBACK TO - `db.savepoint()`, etc.
|
||||||
|
5. ✅ BEGIN EXCLUSIVE - `db.beginExclusive()`
|
||||||
|
|
||||||
|
### Fase 2B - Prioridad Alta ✅ COMPLETADA
|
||||||
|
1. ✅ Backup API completo - `Backup` struct, `backupToFile()`, `loadFromFile()`
|
||||||
|
2. ✅ User-defined functions (scalar) - `db.createScalarFunction()`
|
||||||
|
3. ✅ Collations personalizadas - `db.createCollation()`
|
||||||
|
4. ✅ ATTACH/DETACH - `db.attach()`, `db.detach()`, `db.listDatabases()`
|
||||||
|
|
||||||
|
### Fase 3A - Prioridad Media (Siguiente)
|
||||||
|
1. ⏳ Blob I/O streaming
|
||||||
|
2. ⏳ Hooks (commit, rollback, update)
|
||||||
|
3. ⏳ Aggregator functions
|
||||||
|
4. ⏳ Mas pragmas
|
||||||
|
|
||||||
|
### Fase 3B - Prioridad Baja
|
||||||
|
1. ⏳ Authorizer
|
||||||
|
2. ⏳ Progress handler
|
||||||
|
3. ⏳ Pre-update hook
|
||||||
|
4. ⏳ Window functions
|
||||||
|
5. ⏳ Limits API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Funciones SQLite C Relevantes (Referencia)
|
||||||
|
|
||||||
|
```c
|
||||||
|
// Conexion
|
||||||
|
sqlite3_open_v2()
|
||||||
|
sqlite3_close_v2()
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
sqlite3_prepare_v2()
|
||||||
|
sqlite3_finalize()
|
||||||
|
sqlite3_reset()
|
||||||
|
sqlite3_clear_bindings()
|
||||||
|
sqlite3_step()
|
||||||
|
sqlite3_sql()
|
||||||
|
sqlite3_expanded_sql()
|
||||||
|
sqlite3_stmt_readonly()
|
||||||
|
|
||||||
|
// Bind
|
||||||
|
sqlite3_bind_parameter_index()
|
||||||
|
sqlite3_bind_parameter_name()
|
||||||
|
sqlite3_bind_null()
|
||||||
|
sqlite3_bind_int64()
|
||||||
|
sqlite3_bind_double()
|
||||||
|
sqlite3_bind_text()
|
||||||
|
sqlite3_bind_blob()
|
||||||
|
sqlite3_bind_zeroblob()
|
||||||
|
|
||||||
|
// Column
|
||||||
|
sqlite3_column_count()
|
||||||
|
sqlite3_column_name()
|
||||||
|
sqlite3_column_type()
|
||||||
|
sqlite3_column_int64()
|
||||||
|
sqlite3_column_double()
|
||||||
|
sqlite3_column_text()
|
||||||
|
sqlite3_column_blob()
|
||||||
|
sqlite3_column_bytes()
|
||||||
|
sqlite3_column_decltype()
|
||||||
|
sqlite3_column_database_name()
|
||||||
|
sqlite3_column_table_name()
|
||||||
|
sqlite3_column_origin_name()
|
||||||
|
|
||||||
|
// Utilidades
|
||||||
|
sqlite3_last_insert_rowid()
|
||||||
|
sqlite3_changes()
|
||||||
|
sqlite3_total_changes()
|
||||||
|
sqlite3_errmsg()
|
||||||
|
sqlite3_errcode()
|
||||||
|
sqlite3_extended_errcode()
|
||||||
|
sqlite3_libversion()
|
||||||
|
sqlite3_libversion_number()
|
||||||
|
sqlite3_db_filename()
|
||||||
|
sqlite3_db_readonly()
|
||||||
|
|
||||||
|
// Busy
|
||||||
|
sqlite3_busy_timeout()
|
||||||
|
sqlite3_busy_handler()
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
sqlite3_commit_hook()
|
||||||
|
sqlite3_rollback_hook()
|
||||||
|
sqlite3_update_hook()
|
||||||
|
sqlite3_preupdate_hook()
|
||||||
|
|
||||||
|
// Funciones
|
||||||
|
sqlite3_create_function_v2()
|
||||||
|
sqlite3_create_collation_v2()
|
||||||
|
sqlite3_create_window_function()
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
sqlite3_backup_init()
|
||||||
|
sqlite3_backup_step()
|
||||||
|
sqlite3_backup_finish()
|
||||||
|
sqlite3_backup_remaining()
|
||||||
|
sqlite3_backup_pagecount()
|
||||||
|
|
||||||
|
// Blob
|
||||||
|
sqlite3_blob_open()
|
||||||
|
sqlite3_blob_close()
|
||||||
|
sqlite3_blob_read()
|
||||||
|
sqlite3_blob_write()
|
||||||
|
sqlite3_blob_bytes()
|
||||||
|
sqlite3_blob_reopen()
|
||||||
|
|
||||||
|
// Control
|
||||||
|
sqlite3_interrupt()
|
||||||
|
sqlite3_limit()
|
||||||
|
sqlite3_progress_handler()
|
||||||
|
sqlite3_set_authorizer()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**© zsqlite - CGo Parity Analysis**
|
||||||
|
*2025-12-08*
|
||||||
1106
src/root.zig
1106
src/root.zig
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue