# Référence API — Catrust

Documentation de tous les modules publics du crate `catrust`.

---

## Structure du crate

```
catrust
├── core/
│   ├── typeside    — Types primitifs (BaseType, Value, Typeside)
│   ├── schema      — Catégorie (Schema, Node, Edge, Path, PathEquation)
│   ├── instance    — Foncteur Schema → Set (Instance, EntityData)
│   ├── mapping     — Foncteur entre schémas (Mapping, EdgeMapping)
│   ├── migrate     — Opérations Δ, Σ, Π
│   ├── validate    — Vérification de cohérence
│   ├── optimize    — Réécriture de chemins (PathOptimizer)
│   ├── query       — Requêtes CQL (CqlQuery, QueryBlock, WhereClause)
│   └── eval        — Évaluateur in-memory
└── backend/
    ├── sql/        — Génération SQL (PostgreSQL, Snowflake, Trino)
    │   └── planner — Planificateur de requêtes SQL
    └── graph/      — Génération Cypher (Neo4j)
```

---

## `core::typeside`

### `BaseType`

Énumération des types primitifs supportés.

```rust
pub enum BaseType {
    String,          // → TEXT / VARCHAR en SQL
    Integer,         // → INTEGER en SQL (i64 en Rust)
    Float,           // → DOUBLE PRECISION en SQL (f64 en Rust)
    Boolean,         // → BOOLEAN en SQL
    Custom(String),  // Type défini par l'utilisateur
}
```

Implémente `Display`, `Debug`, `Clone`, `PartialEq`, `Eq`, `Hash`.

---

### `Value`

Une valeur concrète dans une instance.

```rust
pub enum Value {
    String(String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Null,
}
```

#### Méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `get_type` | `(&self) -> BaseType` | Retourne le `BaseType` correspondant |

---

### `Typeside`

Ensemble des types + opérations disponibles. Constitue le "socle" sur lequel les schémas sont construits.

```rust
pub struct Typeside {
    pub types: Vec<BaseType>,
    pub operations: Vec<OpSignature>,
    pub constants: HashMap<String, (BaseType, Value)>,
}
```

#### Méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `default_sql` | `() -> Self` | Crée un typeside standard (String, Int, Float, Bool) |
| `empty` | `() -> Self` | Crée un typeside vide |
| `has_type` | `(&self, ty: &BaseType) -> bool` | Vérifie si un type existe |
| `add_type` | `(&mut self, ty: BaseType)` | Ajoute un type |
| `add_operation` | `(&mut self, op: OpSignature)` | Ajoute une opération |

---

## `core::schema`

### `Node`

Un nœud dans la catégorie = une entité = une table.

```rust
pub struct Node {
    pub name: String,
}
```

---

### `Edge`

Une arête dans la catégorie. Deux variantes :

```rust
pub enum Edge {
    ForeignKey { name: String, source: String, target: String },
    Attribute  { name: String, source: String, target: BaseType },
}
```

#### Constructeurs

```rust
Edge::fk("works_in", "Employee", "Department")
Edge::attr("emp_name", "Employee", BaseType::String)
```

#### Méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `name` | `(&self) -> &str` | Nom de l'arête |
| `source` | `(&self) -> &str` | Nom du nœud source |

---

### `Path`

Un chemin dans la catégorie = composition de morphismes.

```rust
pub struct Path {
    pub start: String,      // Nœud de départ
    pub edges: Vec<String>, // Séquence de noms d'arêtes
}
```

**Exemple** : `Path::new("Employee", vec!["works_in", "dept_name"])` représente `Employee.works_in.dept_name`, soit le nom du département de l'employé.

#### Constructeurs et méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `new` | `(start: &str, edges: Vec<&str>) -> Self` | Crée un chemin |
| `identity` | `(node: &str) -> Self` | Chemin identité (longueur 0) |
| `compose` | `(&self, other: &Path) -> Self` | Compose deux chemins (f puis g) |
| `len` | `(&self) -> usize` | Longueur du chemin (nombre d'arêtes) |
| `is_identity` | `(&self) -> bool` | Vrai si chemin identité |

Implémente `Display` : `Employee.works_in.dept_name` ou `id_Employee` pour l'identité.

---

### `PathEquation`

Contrainte d'égalité entre deux chemins.

```rust
pub struct PathEquation {
    pub lhs: Path,  // côté gauche
    pub rhs: Path,  // côté droit
}
```

---

### `Schema`

La catégorie principale. Contient nœuds, arêtes et équations.

```rust
pub struct Schema {
    pub name: String,
    pub nodes: HashMap<String, Node>,
    pub edges: HashMap<String, Edge>,
    pub path_equations: Vec<PathEquation>,
}
```

#### Construction (builder pattern)

Toutes les méthodes `add_*` retournent `&mut Self` pour le chaînage :

```rust
let mut schema = Schema::new("MySchema");
schema
    .add_node("A")
    .add_node("B")
    .add_fk("f", "A", "B")
    .add_attribute("x", "A", BaseType::Integer)
    .add_path_equation(
        Path::new("A", vec!["f", "y"]),
        Path::new("A", vec!["z"]),
    );
```

#### Méthodes de requête

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `foreign_keys` | `(&self) -> Vec<&Edge>` | Toutes les FK |
| `attributes` | `(&self) -> Vec<&Edge>` | Tous les attributs |
| `edges_from` | `(&self, node: &str) -> Vec<&Edge>` | Arêtes sortant d'un nœud |
| `fks_targeting` | `(&self, node: &str) -> Vec<&Edge>` | FK pointant vers un nœud |
| `attributes_of` | `(&self, node: &str) -> Vec<&Edge>` | Attributs d'un nœud |

Implémente `Display` au format CQL.

---

## `core::instance`

### `RowId`

```rust
pub type RowId = u64;
```

Identifiant unique d'une ligne dans une entité. Auto-incrémenté à partir de 1.

---

### `EntityData`

Données d'une entité (une table concrète).

```rust
pub struct EntityData {
    pub attribute_values: HashMap<RowId, HashMap<String, Value>>,
    pub fk_values: HashMap<RowId, HashMap<String, RowId>>,
}
```

#### Méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `insert` | `(&mut self, attrs, fks) -> RowId` | Insère une ligne, retourne son ID |
| `insert_with_id` | `(&mut self, id, attrs, fks)` | Insère avec un ID spécifique |
| `len` | `(&self) -> usize` | Nombre de lignes |
| `is_empty` | `(&self) -> bool` | Vrai si vide |
| `row_ids` | `(&self) -> Vec<RowId>` | Tous les identifiants |
| `get_attr` | `(&self, row_id, attr_name) -> Option<&Value>` | Valeur d'un attribut |
| `get_fk` | `(&self, row_id, fk_name) -> Option<RowId>` | Cible d'une FK |

---

### `Instance`

Un foncteur `Schema → Set`. La structure de données centrale.

```rust
pub struct Instance {
    pub name: String,
    pub schema_name: String,
    pub data: HashMap<String, EntityData>,  // entity_name → EntityData
}
```

#### Méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `new` | `(name: &str, schema: &Schema) -> Self` | Crée une instance vide |
| `insert` | `(&mut self, entity, attrs, fks) -> RowId` | Insère une ligne |
| `follow_path` | `(&self, start_entity, start_row, fk_path, schema) -> Option<RowId>` | Évalue la composition de FK |
| `total_rows` | `(&self) -> usize` | Nombre total de lignes |
| `display` | `(&self, schema: &Schema) -> String` | Affichage lisible |

`follow_path` est l'opération clé : elle évalue `I(g ∘ f)` en suivant la chaîne de FK.

---

## `core::mapping`

### `EdgeMapping`

Comment une arête de S est mappée dans T.

```rust
pub enum EdgeMapping {
    FkToPath(Path),              // FK → chemin de FK dans T
    AttrToPath {
        fk_path: Vec<String>,   // FK intermédiaires dans T
        attr_name: String,       // attribut final dans T
    },
}
```

---

### `Mapping`

Un foncteur `source_schema → target_schema`.

```rust
pub struct Mapping {
    pub name: String,
    pub source_schema_name: String,
    pub target_schema_name: String,
    pub node_mapping: HashMap<String, String>,       // node_S → node_T
    pub edge_mapping: HashMap<String, EdgeMapping>,  // edge_S → EdgeMapping
}
```

#### Méthodes de construction (builder pattern)

```rust
let mut m = Mapping::new("F", "S", "T");
m
    .map_node("PersonS", "EmployeeT")
    .map_fk("friend_of", Path::new("EmployeeT", vec!["knows"]))
    .map_attr_direct("name", "full_name")        // même chemin direct
    .map_attr_via_fk("email", vec!["contact"], "email_addr");  // via FK
```

| Méthode | Description |
|---------|-------------|
| `map_node(src, tgt)` | Mappe un nœud source vers un nœud cible |
| `map_fk(name, path)` | Mappe une FK vers un chemin dans T |
| `map_attr_direct(src, tgt)` | Mappe un attribut vers un attribut de même niveau |
| `map_attr_via_fk(src, fks, attr)` | Mappe un attribut via une chaîne de FK |
| `validate(schema_s, schema_t)` | Vérifie la fonctorialité (`Result<(), String>`) |

---

## `core::migrate`

Les trois opérations fondamentales de migration.

### `delta`

```rust
pub fn delta(
    mapping: &Mapping,
    source_schema: &Schema,
    target_schema: &Schema,
    target_instance: &Instance,
) -> Instance
```

**Pullback** : restructure une instance de T selon la structure de S. Analogie SQL : `SELECT … JOIN`.

### `sigma`

```rust
pub fn sigma(
    mapping: &Mapping,
    source_schema: &Schema,
    target_schema: &Schema,
    source_instance: &Instance,
) -> Instance
```

**Left Kan Extension** : pousse une instance de S vers T. Analogie SQL : `INSERT INTO T SELECT … FROM S`.

---

## `core::validate`

Fonctions de vérification de cohérence.

```rust
pub fn validate_schema(schema: &Schema) -> Result<(), Vec<String>>
pub fn validate_instance(instance: &Instance, schema: &Schema) -> Result<(), Vec<String>>
```

`validate_schema` vérifie :
- Que toutes les FK référencent des nœuds existants
- Que les path equations référencent des arêtes existantes

`validate_instance` vérifie :
- Que toutes les FK pointent vers des `RowId` valides
- Que la propriété fonctorielle est respectée

---

## `core::optimize`

### `RewriteRule`

```rust
pub struct RewriteRule {
    pub lhs: Path,    // chemin long (pattern à matcher)
    pub rhs: Path,    // chemin court (remplacement)
    pub name: String, // description de la règle
}
```

---

### `OptimizationResult`

```rust
pub struct OptimizationResult {
    pub original: Path,
    pub optimized: Path,
    pub joins_eliminated: usize,
    pub rules_applied: Vec<String>,
}
```

---

### `PathOptimizer`

Optimiseur de chemins par réécriture de termes.

```rust
pub struct PathOptimizer { /* ... */ }
```

#### Méthodes

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `from_schema` | `(schema: &Schema) -> Self` | Construit l'optimiseur à partir des path equations |
| `optimize` | `(&self, path: &Path) -> OptimizationResult` | Optimise un chemin |
| `analyze_schema` | `(&self, schema: &Schema) -> Vec<OptimizationResult>` | Analyse toutes les optimisations possibles |

---

## `core::query`

### `CqlQuery`

Une requête CQL : collection de `QueryBlock`.

```rust
pub struct CqlQuery {
    pub name: String,
    pub source_schema_name: String,
    pub blocks: Vec<QueryBlock>,
}
```

```rust
let mut q = CqlQuery::new("MaRequête", "MonSchema");
q.add_block(block);
```

---

### `QueryBlock`

Un bloc `FROM … WHERE … RETURN` d'une requête.

```rust
pub struct QueryBlock {
    pub target_entity: String,
    pub from_vars: HashMap<String, String>,          // var → entity_name
    pub where_clauses: Vec<WhereClause>,
    pub attribute_bindings: HashMap<String, AttributeBinding>,
    pub fk_bindings: HashMap<String, FkBinding>,
}
```

---

### `WhereClause`

```rust
pub enum WhereClause {
    Comparison {
        var: String,        // variable FROM ("e")
        path: Vec<String>,  // chemin + attribut final (["works_in", "dept_name"])
        op: CompOp,
        value: Value,
    },
    // (autres variantes à venir)
}
```

---

### `CompOp`

```rust
pub enum CompOp { Eq, Ne, Lt, Le, Gt, Ge }
```

---

### `AttributeBinding`

```rust
pub struct AttributeBinding {
    pub from_var: String,    // variable FROM
    pub path: Vec<String>,   // FK intermédiaires (peut être vide)
    pub attribute: String,   // attribut final
}
```

---

## `core::eval`

Évaluation de requêtes CQL directement en mémoire.

### `EvalResult`

```rust
pub struct EvalResult {
    pub instance: Instance,   // les données résultat
    pub rows_scanned: usize,  // lignes lues (avant filtre)
    pub rows_returned: usize, // lignes retournées (après filtre)
    pub eval_time_us: u128,   // durée en microsecondes
}
```

Implémente `Display`.

### Fonctions

| Fonction | Signature | Description |
|----------|-----------|-------------|
| `eval_query` | `(query, instance, schema) -> Result<EvalResult, String>` | Évalue une requête |
| `count` | `(result, entity) -> usize` | COUNT(*) sur une entité du résultat |
| `sum` | `(result, entity, attr) -> Value` | SUM sur un attribut numérique |
| `min_val` | `(result, entity, attr) -> Option<Value>` | MIN |
| `max_val` | `(result, entity, attr) -> Option<Value>` | MAX |
| `distinct` | `(result, entity, attr) -> Vec<Value>` | DISTINCT sur un attribut |

---

## `backend`

### Trait `Backend`

Implémenté par tous les backends.

```rust
pub trait Backend {
    fn deploy_schema(&self, schema: &Schema) -> Vec<Statement>;
    fn export_instance(&self, schema: &Schema, instance: &Instance) -> Vec<Statement>;
    fn migrate_delta(&self, mapping: &Mapping, schema_s: &Schema, schema_t: &Schema) -> Vec<Statement>;
    fn migrate_sigma(&self, mapping: &Mapping, schema_s: &Schema, schema_t: &Schema) -> Vec<Statement>;
}
```

### `Statement`

```rust
pub type Statement = String;
```

---

## `backend::sql`

### Dialectes disponibles

| Type | Dialecte | Notes |
|------|----------|-------|
| `PostgresDialect` | PostgreSQL | `SERIAL`, `TEXT`, `BOOLEAN` |
| `SnowflakeDialect` | Snowflake | `AUTOINCREMENT`, `VARCHAR`, types Snowflake |
| `TrinoDialect` | Trino / ex-Presto | Catalogue Iceberg, `BIGINT`, `VARCHAR` |

### `SqlBackend<D: SqlDialect>`

```rust
let backend = SqlBackend::new(PostgresDialect);
let ddl = backend.deploy_schema(&schema);
let dml = backend.export_instance(&schema, &instance);
```

### `SqlPlanner`

Génère des requêtes SQL optimisées depuis des `CqlQuery`.

```rust
let planner = SqlPlanner::new(&PostgresDialect, &schema);
let plans = planner.plan_query(&query);
for plan in &plans {
    println!("{}", plan);  // SELECT optimisé avec JOINs réduits
}
```

---

## `backend::graph`

### `Neo4jBackend`

```rust
let backend = Neo4jBackend::new();
let schema_cypher = backend.deploy_schema(&schema);
let data_cypher   = backend.export_instance(&schema, &instance);
```

Génère du **Cypher** :
- Nœuds CQL → `CREATE (:Label { props })`
- FK CQL → `MATCH ... CREATE (a)-[:REL]->(b)`
- Contraintes d'unicité → `CREATE CONSTRAINT ON ...`

---

## `catrustdb-store` — Column store natif

Le crate `catrustdb-store` fournit le moteur de stockage colonnaire utilisé par le serveur TCP et le CLI.

### `Val`

Valeur native du column store (distincte de `core::typeside::Value`).

```rust
pub enum Val {
    Str(String),
    Int(i64),
    Float(f64),
    Bool(bool),
    Date(String),   // ISO-8601
    Uuid(String),
    Null,
}
```

---

### `EntityStore`

Stockage colonnaire pour une entité. Chaque attribut est un `Vec<Option<T>>` ; les FK sont des `Vec<Option<RowId>>`.

```rust
pub struct EntityStore {
    pub name: String,
    pub attr_cols:  Vec<(String, AttrCol)>,
    pub fk_cols:    Vec<(String, Vec<Option<RowId>>)>,
    // champs privés : deleted bitmap, index eq/range, dirty sets
}
```

#### Écriture

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `new` | `(name: impl Into<String>) -> Self` | Crée un store vide |
| `insert_row` | `(&mut self, attrs: &HashMap<String,Val>, fks: &HashMap<String,RowId>)` | Insère une ligne, maintient les index actifs |
| `delete_row` | `(&mut self, row_id: RowId) -> bool` | Soft-delete |
| `update_attrs` | `(&mut self, row_id: RowId, attrs: HashMap<String,Val>)` | Mise à jour in-place + rafraîchissement index |

#### Lecture

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `all_rows` | `(&self) -> impl Iterator<Item=RowId>` | Tous les RowId (y compris supprimés) |
| `all_active_rows` | `(&self) -> impl Iterator<Item=RowId>` | RowId non supprimés |
| `is_deleted` | `(&self, row_id: RowId) -> bool` | Test de suppression |
| `get_attr` | `(&self, row_id: RowId, attr: &str) -> Val` | Valeur d'un attribut |
| `get_fk` | `(&self, row_id: RowId, fk: &str) -> Option<RowId>` | Cible d'une FK |
| `len` | `(&self) -> usize` | Total lignes (inclut supprimées) |
| `active_len` | `(&self) -> usize` | Lignes actives |

#### Index d'égalité

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `build_index` | `(&mut self, attr: &str)` | Construit un index `HashMap<Val, Vec<RowId>>` |
| `drop_index` | `(&mut self, attr: &str)` | Supprime l'index |
| `has_index` | `(&self, attr: &str) -> bool` | Vrai si index actif **ou en attente de build (lazy)** |
| `lookup_eq` | `(&self, attr: &str, val: &Val) -> &[RowId]` | Lookup O(1) |
| `ensure_index` | `(&mut self, attr: &str)` | Build à la demande si marqué dirty (lazy) |

#### Index de plage

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `build_range_index` | `(&mut self, attr: &str)` | Construit un `BTreeMap<OrdVal, Vec<RowId>>` |
| `has_range_index` | `(&self, attr: &str) -> bool` | Vrai si index actif **ou en attente (lazy)** |
| `lookup_range` | `(&self, attr, lo, hi) -> Vec<RowId>` | Lookup O(log n) sur plage |
| `ensure_range_index` | `(&mut self, attr: &str)` | Build à la demande si marqué dirty (lazy) |

#### Lazy indexing

```rust
// Après from_binary_v2 : load ~5 ms, zéro index construit
let mut db = Database::from_binary_v2(path)?;

// Premier filtre sur "salary" → déclenche build_index("salary") uniquement
let rich = filter_entity_lazy(&mut db, "Employee", &Pred::Eq {
    path: vec!["salary".into()],
    val:  Val::Float(90000.0),
});
// Requêtes suivantes sur "salary" → O(1) via index en cache
```

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `mark_all_indexes_dirty` | `(&mut self)` | Vide les index, marque toutes les colonnes pour rebuild lazy |

---

### `Database`

Conteneur multi-entités. Wrapp les `EntityStore` et expose les opérations de persistence.

```rust
pub struct Database {
    pub schema_cql: String,
    // stores: HashMap<String, EntityStore>  (privé)
}
```

#### Construction

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `new` | `(schema: Schema) -> Self` | Base vide |
| `with_cql` | `(schema: Schema, cql: String) -> Self` | Base avec CQL stocké |
| `load_instance` | `(&mut self, inst: &Instance) -> Result<()>` | Charge une `core::Instance` |

#### Accès aux stores

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `entity` | `(&self, name: &str) -> Option<&EntityStore>` | Lecture |
| `entity_mut` | `(&mut self, name: &str) -> Option<&mut EntityStore>` | Écriture |
| `entity_names` | `(&self) -> impl Iterator<Item=&str>` | Noms des entités |
| `total_rows` | `(&self) -> usize` | Total lignes actives |

#### Index globaux

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `rebuild_all_indexes` | `(&mut self)` | Construit tous les index (eager) — O(n×cols) |
| `rebuild_all_indexes_lazy` | `(&mut self)` | Marque tous les index dirty — O(cols), pas O(n) |

#### Persistence WAL (V1)

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `persist` | `(&self, path: &Path) -> io::Result<()>` | Sauvegarde WAL NDJSON |
| `from_wal` | `(path: &Path) -> io::Result<Self>` | Rechargement WAL |
| `from_wal_encrypted` | `(path, cipher) -> io::Result<Self>` | WAL chiffré AES-256-GCM |

#### Persistence binaire V1 (bincode)

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `persist_binary` | `(&self, path: &Path) -> io::Result<()>` | Snapshot bincode row-oriented |
| `from_binary` | `(path: &Path) -> io::Result<Self>` | Rechargement (rebuild indexes eager) |

#### Persistence binaire V2 (colonnaire + memmap2) ⭐

| Méthode | Signature | Description |
|---------|-----------|-------------|
| `persist_binary_v2` | `(&self, path: &Path) -> io::Result<()>` | Snapshot colonnaire — **13.5×** plus rapide que V1 |
| `from_binary_v2` | `(path: &Path) -> io::Result<Self>` | Chargement memmap2 + **lazy indexing** — **~700×** plus rapide |

**Format V2** — magic `CATRUSTDB\x02` :
```
u32 schema_len | schema_cql | u32 nb_entities
  [entity: str name | u64 row_count | u32 nb_attrs
    [attr: str name | u8 type_tag | bitmap_null | payload_colonnaire]
   u32 nb_fks
    [fk: str name | bitmap | u64[] values]]
```

`type_tag` : 0=Str, 1=Int, 2=Float, 3=Bool, 4=Date, 5=Uuid.
Int/Float : packed LE bytes (zéro alloc). Str/Date/Uuid : offset array + data blob.

```rust
// Sauvegarder 1 M lignes en ~93 ms
db.persist_binary_v2(Path::new("/tmp/snapshot.v2"))?;

// Charger en ~5 ms (index bâtis à la demande)
let mut db = Database::from_binary_v2(Path::new("/tmp/snapshot.v2"))?;
```

---

## `catrustdb-eval` — Évaluateur natif

### `Pred`

Prédicat de filtre.

```rust
pub enum Pred {
    Eq  { path: Vec<String>, val: Val },
    Ne  { path: Vec<String>, val: Val },
    Gt  { path: Vec<String>, val: Val },
    Ge  { path: Vec<String>, val: Val },
    Lt  { path: Vec<String>, val: Val },
    Le  { path: Vec<String>, val: Val },
    And(Box<Pred>, Box<Pred>),
    Or(Box<Pred>,  Box<Pred>),
    Not(Box<Pred>),
    True,
}
```

---

### `filter_entity`

```rust
pub fn filter_entity(db: &Database, entity: &str, pred: &Pred) -> Vec<RowId>
```

Filtre une entité. Fast-paths automatiques :
- `Pred::Eq` + index d'égalité → **O(1)**
- `Pred::Gt/Ge/Lt/Le` + index de plage → **O(log n)**
- Fallback → scan O(n)

---

### `filter_entity_lazy` ⭐

```rust
pub fn filter_entity_lazy(db: &mut Database, entity: &str, pred: &Pred) -> Vec<RowId>
```

Identique à `filter_entity` mais déclenche le build d'index à la demande avant le filtre.
À utiliser après `from_binary_v2` (qui active le lazy indexing).

```rust
// Après from_binary_v2 — aucun index construit
let mut db = Database::from_binary_v2(path)?;

// Premier appel : construit l'index "salary" en O(n), puis filtre en O(1)
let rich = filter_entity_lazy(&mut db, "Employee", &Pred::Eq {
    path: vec!["salary".into()],
    val:  Val::Float(90000.0),
});

// Deuxième appel : O(1) direct (index déjà en cache)
let rich2 = filter_entity_lazy(&mut db, "Employee", &Pred::Ge {
    path: vec!["salary".into()],
    val:  Val::Float(70000.0),
});
```

---

### `eval_cql_query`

```rust
pub fn eval_cql_query(db: &Database, query: &CqlQuery) -> QueryResult
```

Évalue une requête CQL complète (FROM / WHERE / SELECT / FK traversal / GROUP BY / ORDER BY / LIMIT).

---

### `sigma_native`

```rust
pub fn sigma_native(
    db: &Database,
    mapping: &Mapping,
    source_schema: &Schema,
    target_db: &mut Database,
) -> Result<(), String>
```

Migration Σ complète en deux phases sur le column store natif.
