---
name: shaderlab_db
kind: function
lang: cpp
domain: gfx
version: "1.0.0"
purity: impure
signature: "bool shaderlab_db_save_generator(GeneratorRecord& gen, std::string* err); std::vector<GeneratorRecord> shaderlab_db_list_generators(); bool shaderlab_db_get_generator(const std::string& id, GeneratorRecord& out); bool shaderlab_db_delete_generator(const std::string& id);"
description: "CRUD persistente para generators custom de shaders_lab via sqlite3. Guarda el GLSL original, el body traducido para el DAG, los DagControl y los param_defaults en una BD local (shaders_lab.db). Soporta open(:memory:) para tests."
tags: [sqlite, shaders, glsl, dag, gfx, persistence, crud, shaders_lab]
uses_functions: []
uses_types:
  - dag_types_cpp_gfx
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [sqlite3, string, vector, chrono, ctime, iomanip, sstream]
tested: true
tests:
  - "open in-memory + list empty"
  - "save + get roundtrip preserva controls/params/defaults"
  - "list ordenado por label"
  - "update preserva created_at y bumps updated_at"
  - "delete devuelve true solo si existia"
test_file_path: "cpp/functions/gfx/shaderlab_db.cpp"
file_path: "cpp/functions/gfx/shaderlab_db.cpp"
params:
  - name: gen
    desc: "Registro del generator. Debe llevar id (snake_case), label, source_glsl original, body_glsl traducido, param_count y los param_defaults/controls coherentes."
  - name: id
    desc: "ID snake_case del generator (clave primaria de la tabla)."
  - name: out
    desc: "Registro de salida en get; queda relleno solo si la funcion devuelve true."
  - name: err
    desc: "Mensaje de error opcional si save falla (constraint, schema, IO)."
output: "Persistencia en la tabla generators de shaders_lab.db. Las listas vienen ordenadas por label."
notes: "consumido por cpp/apps/shaders_lab/main.cpp"
---

## Schema

```sql
CREATE TABLE generators (
  id              TEXT PRIMARY KEY,
  label           TEXT NOT NULL,
  description     TEXT NOT NULL DEFAULT '',
  source_glsl     TEXT NOT NULL,
  body_glsl       TEXT NOT NULL,
  param_count     INTEGER NOT NULL,
  param_defaults  TEXT NOT NULL,   -- CSV de floats
  param_names     TEXT NOT NULL,   -- LF-separated strings
  controls        TEXT NOT NULL,   -- LF-separated, fields '|': kind|label|p0|p1|p2|min|max|step
  tags            TEXT NOT NULL DEFAULT '',
  created_at      TEXT NOT NULL,
  updated_at      TEXT NOT NULL
);
```

## Notas

- Formato de serializacion custom (no JSON): CSV para floats, LF para strings, `|` para fields de control. Sin escape de separadores; los labels no deben contener `\n` ni `|`.
- `shaderlab_db_open` es idempotente: re-abrir con el mismo path es no-op.
- `:memory:` como path crea una BD temporal en memoria (usado en tests).
- `save_generator` hace upsert (`ON CONFLICT DO UPDATE`). Setea `created_at` solo si esta vacio.
- Tests inline activables con `-DSHADERLAB_DB_TEST` y linkando sqlite3 (amalgamation o `-lsqlite3`).
