Migraciones
Migraciones
Sección titulada «Migraciones»El modulo wippy/migration proporciona un framework de migracion de bases de datos con un pequeno DSL para definir cambios de esquema, un runner que las descubre y ejecuta, y un bootloader que ejecuta las migraciones pendientes para cada target_db registrado en el proyecto.
Las migraciones soportan SQLite, PostgreSQL y MySQL, con implementaciones up/down por driver definidas lado a lado.
Configuracion
Sección titulada «Configuracion»Agrega el modulo a tu proyecto:
wippy add wippy/migrationwippy installDeclara la dependencia y la base de datos de la aplicacion a la que se dirigen las migraciones:
version: "1.0"namespace: app
entries: - name: app_db kind: db.sql.sqlite path: ./data/app.db
- name: dep.migration kind: ns.dependency component: wippy/migration version: "*"El bootloader de migracion se registra con wippy/bootloader en el orden 20. Cuando la aplicacion inicia, descubre cada entrada de migracion en el registro, las agrupa por meta.target_db y ejecuta las migraciones pendientes contra cada base de datos.
Definir una Migracion
Sección titulada «Definir una Migracion»Una migracion es una entrada function.lua con meta.type: migration. La entrada retorna una funcion producida por migration.define(...).
entries: - name: 01_create_users_table kind: function.lua meta: type: migration target_db: app:app_db timestamp: "2025-01-15T10:00:00Z" source: file://01_create_users_table.lua imports: migration: wippy.migration:migrationreturn require("migration").define(function() migration("Create users table", function() database("sqlite", function() up(function(db) local ok, err = db:execute([[ CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE ) ]]) if err then error(err) end end)
down(function(db) db:execute("DROP TABLE IF EXISTS users") end) end)
database("postgres", function() up(function(db) db:execute([[ CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE ) ]]) end)
down(function(db) db:execute("DROP TABLE IF EXISTS users") end) end) end)end)Metadatos Requeridos
Sección titulada «Metadatos Requeridos»| Campo | Requerido | Descripcion |
|---|---|---|
meta.type | si | Debe ser "migration" para el descubrimiento |
meta.target_db | si | ID de registro de la base de datos contra la que ejecutar |
meta.timestamp | no | Marca de tiempo ISO-8601 usada para ordenar cuando multiples migraciones apuntan a la misma base de datos |
meta.tags | no | Array de etiquetas; el runner puede filtrar migraciones por etiqueta |
Las migraciones para una base de datos se ejecutan en orden ascendente de meta.timestamp.
Dentro de la funcion pasada a migration.define, hay tres funciones anidadas disponibles:
| Funcion | Descripcion |
|---|---|
migration(description, fn) | Abrir una nueva migracion con una descripcion legible |
database(type, fn) | Declarar una implementacion para "sqlite", "postgres" o "mysql" |
up(fn) / down(fn) | Definir funciones de avance y reversion |
after(fn) | Hook opcional post-migracion (misma transaccion) |
Cada funcion up/down/after recibe un objeto de transaccion, no una conexion cruda. Las tres operaciones se ejecutan en una sola transaccion que se revierte en caso de error.
Metodos de Transaccion
Sección titulada «Metodos de Transaccion»local rows, err = db:query(sql, params) -- SELECT, retorna array de filaslocal result, err = db:execute(sql, params) -- INSERT/UPDATE/DDL, retorna { rows_affected, last_insert_id }local stmt, err = db:prepare(sql) -- sentencia preparadaUsa siempre consultas parametrizadas:
db:execute("INSERT INTO users (name, email) VALUES (?, ?)", { "Alice", "alice@example.com" })Manejo de Errores
Sección titulada «Manejo de Errores»Llamar a error(...) aborta la migracion y revierte la transaccion. Envuelve cada sentencia que pueda fallar:
up(function(db) local _, err = db:execute("CREATE TABLE ...") if err then error(err) endend)API del Runner
Sección titulada «API del Runner»El runner se expone como una biblioteca para uso programatico:
imports: runner: wippy.migration:runnerlocal runner = require("runner").setup("app:app_db")
local result = runner:run() -- aplicar todas las migraciones pendienteslocal result = runner:run_next() -- aplicar la siguiente migracion pendientelocal result = runner:rollback({ id = "app:01_create_users_table" })local status = runner:status() -- listar migraciones aplicadas + pendientesrunner:run(options)
Sección titulada «runner:run(options)»Aplica cada migracion pendiente para la base de datos configurada. Retorna un resumen:
{ status = "complete", -- "complete" o "error" migrations_found = 3, migrations_applied = 2, migrations_skipped = 1, migrations_failed = 0, duration = 0.123, migrations = { ... }, -- estado por migracion skipped_details = { ... },}Opciones:
| Opcion | Descripcion |
|---|---|
tags | Array de etiquetas; solo se consideran las migraciones cuyos meta.tags se intersecten |
runner:rollback(options)
Sección titulada «runner:rollback(options)»Revierte una sola migracion por id (requerido):
runner:rollback({ id = "app:01_create_users_table" })runner:status(options)
Sección titulada «runner:status(options)»Retorna { applied = {...}, pending = {...} }, ordenado por applied_at y meta.timestamp respectivamente.
API de Registry
Sección titulada «API de Registry»wippy.migration:registry ofrece consultas directas al registro:
| Funcion | Descripcion |
|---|---|
registry.find({ target_db, tags }) | Retorna todas las entradas de migracion que coinciden con los criterios |
registry.get(id) | Retorna una sola entrada de migracion por id |
registry.get_target_dbs() | Retorna cada meta.target_db unico presente en las migraciones |
registry.get_tags() | Retorna cada etiqueta unica presente en las migraciones |
El bootloader las usa para descubrir el conjunto completo de bases de datos destino al inicio.
Seguimiento de Migraciones
Sección titulada «Seguimiento de Migraciones»El runner crea una tabla wippy_migrations en cada base de datos destino en la primera ejecucion. Las migraciones aplicadas se registran por id para que las ejecuciones posteriores las omitan. La tabla de seguimiento se crea automaticamente; no escribas tu propia migracion para crearla.
Buenas Practicas
Sección titulada «Buenas Practicas»- Un cambio logico por migracion - crear una tabla, agregar una columna, crear un indice.
- Escribe un
downreal - si la reversion es imposible (perdida de datos), documentalo y lanza un error en lugar de tener exito silenciosamente. - Prefiere la idempotencia -
CREATE TABLE IF NOT EXISTSyDROP TABLE IF EXISTSsobreviven a re-ejecuciones sin manejo especial. - Mantener DDL y DML separados - no siembres datos en la misma migracion que crea una tabla cuando puedas evitarlo.
- Probar ambas direcciones - aplica la migracion, revierte y verifica que el esquema coincida con el estado inicial.
Ver Tambien
Sección titulada «Ver Tambien»- SQL Driver - Configuracion del recurso de base de datos
- Bootloader - Ordenamiento y hooks del bootloader
- Vision General del Framework - Uso de modulos del framework