feat(database): finalize database architecture v1
This commit is contained in:
parent
fbe1cd11fb
commit
142088da87
16 changed files with 5287 additions and 56 deletions
6
Makefile
6
Makefile
|
|
@ -62,12 +62,14 @@ $(TEST_TREE_BUILDER): \
|
|||
$(TEST_PROJECT): \
|
||||
tests/test_investigation_project.c \
|
||||
src/core/investigation_project.c \
|
||||
src/database/database.c
|
||||
src/database/database.c \
|
||||
src/database/schema.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
||||
|
||||
$(TEST_DATABASE): \
|
||||
tests/test_database.c \
|
||||
src/database/database.c
|
||||
src/database/database.c \
|
||||
src/database/schema.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
||||
|
||||
test: \
|
||||
|
|
|
|||
1231
database/schema_v1.sql
Normal file
1231
database/schema_v1.sql
Normal file
File diff suppressed because it is too large
Load diff
302
docs/CONVENTIONS.md
Normal file
302
docs/CONVENTIONS.md
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
# Conventions de développement
|
||||
|
||||
Version : 1.0
|
||||
|
||||
---
|
||||
|
||||
# Philosophie
|
||||
|
||||
Labfy Investigation est développé comme un logiciel d'enquête numérique.
|
||||
|
||||
Les choix d'architecture privilégient :
|
||||
|
||||
- la lisibilité ;
|
||||
- la maintenabilité ;
|
||||
- la robustesse ;
|
||||
- la traçabilité ;
|
||||
- la simplicité.
|
||||
|
||||
Une solution simple et cohérente est toujours préférée à une solution
|
||||
complexe.
|
||||
|
||||
---
|
||||
|
||||
# Langue
|
||||
|
||||
Le domaine métier est écrit en français.
|
||||
|
||||
Exemples :
|
||||
|
||||
- preuve
|
||||
- entite
|
||||
- personne
|
||||
- source
|
||||
- chronologie
|
||||
- journal
|
||||
- hypothese
|
||||
- recherche
|
||||
|
||||
Les standards techniques conservent leur nom d'origine.
|
||||
|
||||
Exemples :
|
||||
|
||||
- uuid
|
||||
- sha256
|
||||
- mime_type
|
||||
- sqlite
|
||||
- created_at
|
||||
- updated_at
|
||||
- relative_path
|
||||
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
Le projet est organisé par domaine métier.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
preuve
|
||||
↓
|
||||
preuve.h
|
||||
preuve.c
|
||||
|
||||
entite
|
||||
↓
|
||||
entite.h
|
||||
entite.c
|
||||
```
|
||||
|
||||
Chaque module possède une responsabilité unique.
|
||||
|
||||
---
|
||||
|
||||
# SQL
|
||||
|
||||
Une table représente un objet métier.
|
||||
|
||||
Une table importante possède :
|
||||
|
||||
- une structure C ;
|
||||
- un module C ;
|
||||
- des tests.
|
||||
|
||||
Les objets métier utilisent :
|
||||
|
||||
```
|
||||
TEXT PRIMARY KEY
|
||||
```
|
||||
|
||||
contenant un UUID.
|
||||
|
||||
Les tables de référence utilisent :
|
||||
|
||||
```
|
||||
INTEGER PRIMARY KEY
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# UUID
|
||||
|
||||
Tous les objets métier utilisent un UUID.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
preuve
|
||||
entite
|
||||
personne
|
||||
source
|
||||
chronologie
|
||||
journal
|
||||
```
|
||||
|
||||
Il n'existe pas simultanément :
|
||||
|
||||
```
|
||||
id
|
||||
uuid
|
||||
```
|
||||
|
||||
La colonne :
|
||||
|
||||
```
|
||||
id
|
||||
```
|
||||
|
||||
contient directement l'UUID.
|
||||
|
||||
---
|
||||
|
||||
# Dates
|
||||
|
||||
Toutes les dates sont enregistrées en UTC.
|
||||
|
||||
Format :
|
||||
|
||||
```
|
||||
YYYY-MM-DDTHH:MM:SSZ
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Chemins
|
||||
|
||||
Les chemins sont toujours relatifs à la racine de l'enquête.
|
||||
|
||||
Autorisé :
|
||||
|
||||
```
|
||||
01_Preuves_Originales/Documents/facture.pdf
|
||||
```
|
||||
|
||||
Interdit :
|
||||
|
||||
```
|
||||
/home/user/Documents/...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Suppression
|
||||
|
||||
Les objets métier importants ne sont jamais supprimés immédiatement.
|
||||
|
||||
Une suppression est une modification d'état.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
active
|
||||
archived
|
||||
deleted
|
||||
```
|
||||
|
||||
Une purge physique éventuelle devra être explicite.
|
||||
|
||||
---
|
||||
|
||||
# Nommage
|
||||
|
||||
Le SQL utilise :
|
||||
|
||||
- snake_case
|
||||
- minuscules
|
||||
- aucun accent
|
||||
- aucun espace
|
||||
|
||||
---
|
||||
|
||||
# Code C
|
||||
|
||||
Norme :
|
||||
|
||||
```
|
||||
C17
|
||||
```
|
||||
|
||||
Variables :
|
||||
|
||||
Toujours explicites.
|
||||
|
||||
Autorisé :
|
||||
|
||||
```c
|
||||
relative_path
|
||||
description
|
||||
commentaire
|
||||
created_at
|
||||
```
|
||||
|
||||
Interdit :
|
||||
|
||||
```c
|
||||
path
|
||||
desc
|
||||
comm
|
||||
crt
|
||||
```
|
||||
|
||||
Les fonctions utilisent le nom du module.
|
||||
|
||||
Exemple :
|
||||
|
||||
```c
|
||||
preuve_create()
|
||||
preuve_load()
|
||||
preuve_update()
|
||||
preuve_delete()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Structures
|
||||
|
||||
Une structure représente un objet métier.
|
||||
|
||||
Exemple :
|
||||
|
||||
```c
|
||||
typedef struct Preuve Preuve;
|
||||
```
|
||||
|
||||
Les structures publiques sont opaques lorsque cela est possible.
|
||||
|
||||
---
|
||||
|
||||
# Tests
|
||||
|
||||
Toute nouvelle fonctionnalité importante possède un test.
|
||||
|
||||
Le développement suit toujours l'ordre :
|
||||
|
||||
```
|
||||
Conception
|
||||
|
||||
↓
|
||||
|
||||
Implémentation
|
||||
|
||||
↓
|
||||
|
||||
Tests
|
||||
|
||||
↓
|
||||
|
||||
Commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Git
|
||||
|
||||
Un ticket terminé correspond à un commit.
|
||||
|
||||
Le message de commit est rédigé en anglais.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
feat(database): initialize investigation database
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Documentation
|
||||
|
||||
Les décisions importantes sont documentées.
|
||||
|
||||
Les changements de schéma SQLite sont précédés d'un audit.
|
||||
|
||||
Aucune décision importante ne doit exister uniquement dans le code.
|
||||
|
||||
---
|
||||
|
||||
# Objectif
|
||||
|
||||
Le projet doit rester compréhensible plusieurs années après sa création.
|
||||
|
||||
La priorité est donnée à la qualité du code plutôt qu'à la rapidité de
|
||||
développement.
|
||||
2909
docs/database/SCHEMA_AUDIT_V1.md
Normal file
2909
docs/database/SCHEMA_AUDIT_V1.md
Normal file
File diff suppressed because it is too large
Load diff
643
docs/tickets/closed/TICKET-023.md
Normal file
643
docs/tickets/closed/TICKET-023.md
Normal file
|
|
@ -0,0 +1,643 @@
|
|||
# Ticket #023
|
||||
|
||||
## Titre
|
||||
|
||||
Consolider le schéma métier initial de l'enquête.
|
||||
|
||||
---
|
||||
|
||||
## Objectif
|
||||
|
||||
Transformer le schéma SQLite préparé lors des premières phases du projet en
|
||||
un schéma métier officiel, cohérent, documenté et testable.
|
||||
|
||||
La base existante sert de fondation.
|
||||
|
||||
Le travail ne consiste pas à repartir de zéro, mais à :
|
||||
|
||||
- conserver les concepts métier déjà définis ;
|
||||
- corriger les incohérences ;
|
||||
- renforcer les contraintes ;
|
||||
- uniformiser les conventions ;
|
||||
- intégrer le schéma au module `Database`.
|
||||
|
||||
---
|
||||
|
||||
## Base de travail
|
||||
|
||||
La base historique contient notamment les tables suivantes :
|
||||
|
||||
```text
|
||||
preuves
|
||||
entites
|
||||
personnes
|
||||
chronologie
|
||||
sources
|
||||
recherche
|
||||
hypotheses
|
||||
journal
|
||||
tags
|
||||
types_preuve
|
||||
types_entite
|
||||
types_source
|
||||
types_outils
|
||||
associations
|
||||
personnes_entites
|
||||
entite_tags
|
||||
preuves_tag
|
||||
```
|
||||
|
||||
Les tables créées au ticket #022 restent également obligatoires :
|
||||
|
||||
```text
|
||||
metadata
|
||||
investigation
|
||||
```
|
||||
|
||||
Le schéma officiel doit donc préserver ces concepts tout en les consolidant.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
InvestigationProject
|
||||
│
|
||||
▼
|
||||
Database
|
||||
│
|
||||
▼
|
||||
Schéma SQLite versionné
|
||||
│
|
||||
├── Métadonnées
|
||||
├── Enquête
|
||||
├── Preuves
|
||||
├── Entités
|
||||
├── Personnes
|
||||
├── Sources
|
||||
├── Chronologie
|
||||
├── Recherches
|
||||
├── Hypothèses
|
||||
├── Journal
|
||||
└── Tags et relations
|
||||
```
|
||||
|
||||
Le module `Database` reste le seul autorisé à exécuter du SQL.
|
||||
|
||||
---
|
||||
|
||||
## Décisions de conception à officialiser
|
||||
|
||||
### Identifiants
|
||||
|
||||
Les entités métier utiliseront des identifiants texte de type UUID.
|
||||
|
||||
Exemple :
|
||||
|
||||
```text
|
||||
550e8400-e29b-41d4-a716-446655440000
|
||||
```
|
||||
|
||||
Les identifiants sont générés par l'application avec :
|
||||
|
||||
```c
|
||||
g_uuid_string_random()
|
||||
```
|
||||
|
||||
Les tables de référence simples peuvent utiliser des identifiants entiers.
|
||||
|
||||
---
|
||||
|
||||
### Dates
|
||||
|
||||
Les dates techniques sont stockées en UTC au format ISO 8601 :
|
||||
|
||||
```text
|
||||
YYYY-MM-DDTHH:MM:SSZ
|
||||
```
|
||||
|
||||
Exemple :
|
||||
|
||||
```text
|
||||
2026-07-15T08:42:17Z
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Nommage
|
||||
|
||||
Les noms SQL utilisent uniquement :
|
||||
|
||||
- des minuscules ;
|
||||
- du `snake_case` ;
|
||||
- aucun accent ;
|
||||
- aucun espace.
|
||||
|
||||
Les noms des tables de liaison sont uniformisés.
|
||||
|
||||
Exemple :
|
||||
|
||||
```text
|
||||
preuve_tags
|
||||
entite_tags
|
||||
personne_entites
|
||||
```
|
||||
|
||||
Aucune forme singulier/pluriel incohérente ne doit subsister.
|
||||
|
||||
---
|
||||
|
||||
### Clés étrangères
|
||||
|
||||
Toutes les relations métier doivent utiliser des clés étrangères explicites.
|
||||
|
||||
Le module `Database` doit activer :
|
||||
|
||||
```sql
|
||||
PRAGMA foreign_keys = ON;
|
||||
```
|
||||
|
||||
à chaque ouverture de connexion.
|
||||
|
||||
Les comportements `ON DELETE` doivent être décidés pour chaque relation.
|
||||
|
||||
---
|
||||
|
||||
### Tables de liaison
|
||||
|
||||
Chaque table de liaison doit empêcher les doublons grâce à :
|
||||
|
||||
- une clé primaire composite ;
|
||||
- ou une contrainte `UNIQUE`.
|
||||
|
||||
Exemple :
|
||||
|
||||
```sql
|
||||
PRIMARY KEY (preuve_id, tag_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tables métier à consolider
|
||||
|
||||
### `preuves`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- représenter un élément de preuve ;
|
||||
- mémoriser son nom ;
|
||||
- son chemin relatif ;
|
||||
- son type ;
|
||||
- sa description ;
|
||||
- ses dates ;
|
||||
- son hash éventuel ;
|
||||
- son statut.
|
||||
|
||||
Contraintes minimales :
|
||||
|
||||
- identifiant obligatoire ;
|
||||
- nom obligatoire ;
|
||||
- chemin relatif obligatoire ;
|
||||
- type obligatoire ;
|
||||
- date de création obligatoire ;
|
||||
- chemin unique au sein d'une enquête.
|
||||
|
||||
---
|
||||
|
||||
### `entites`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- représenter une entité identifiée pendant l'enquête.
|
||||
|
||||
Exemples :
|
||||
|
||||
- adresse email ;
|
||||
- compte bancaire ;
|
||||
- profil social ;
|
||||
- pseudonyme ;
|
||||
- téléphone ;
|
||||
- document d'identité.
|
||||
|
||||
Contraintes minimales :
|
||||
|
||||
- identifiant obligatoire ;
|
||||
- type obligatoire ;
|
||||
- valeur obligatoire ;
|
||||
- date de création obligatoire.
|
||||
|
||||
---
|
||||
|
||||
### `personnes`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- représenter une personne physique ou un profil humain étudié.
|
||||
|
||||
Le modèle ne doit pas obliger à connaître l'identité civile complète.
|
||||
|
||||
Une personne peut n'avoir qu'un pseudonyme ou une désignation temporaire.
|
||||
|
||||
---
|
||||
|
||||
### `sources`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- représenter l'origine d'une information ou d'une preuve.
|
||||
|
||||
Exemples :
|
||||
|
||||
- site web ;
|
||||
- email ;
|
||||
- réseau social ;
|
||||
- document ;
|
||||
- témoignage ;
|
||||
- outil OSINT.
|
||||
|
||||
---
|
||||
|
||||
### `chronologie`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- mémoriser les événements importants de l'enquête ;
|
||||
- relier éventuellement un événement à une preuve, une entité ou une source.
|
||||
|
||||
---
|
||||
|
||||
### `recherches`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- documenter une opération de recherche ;
|
||||
- mémoriser la requête ;
|
||||
- l'outil employé ;
|
||||
- la date ;
|
||||
- le résultat ;
|
||||
- les observations.
|
||||
|
||||
---
|
||||
|
||||
### `hypotheses`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- enregistrer une hypothèse de travail ;
|
||||
- mémoriser son statut ;
|
||||
- sa justification ;
|
||||
- les éléments qui la soutiennent ou la contredisent.
|
||||
|
||||
---
|
||||
|
||||
### `journal`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- conserver la trace des actions significatives réalisées dans l'enquête.
|
||||
|
||||
Exemples :
|
||||
|
||||
- création d'une preuve ;
|
||||
- modification d'une entité ;
|
||||
- calcul d'un hash ;
|
||||
- génération d'un rapport ;
|
||||
- import d'un fichier.
|
||||
|
||||
---
|
||||
|
||||
### `tags`
|
||||
|
||||
Responsabilité :
|
||||
|
||||
- permettre une classification libre des preuves et des entités.
|
||||
|
||||
---
|
||||
|
||||
## Tables de référence
|
||||
|
||||
Les tables suivantes doivent être consolidées :
|
||||
|
||||
```text
|
||||
types_preuve
|
||||
types_entite
|
||||
types_source
|
||||
types_outils
|
||||
```
|
||||
|
||||
Elles doivent comporter au minimum :
|
||||
|
||||
```sql
|
||||
id INTEGER PRIMARY KEY
|
||||
code TEXT NOT NULL UNIQUE
|
||||
label TEXT NOT NULL
|
||||
description TEXT
|
||||
```
|
||||
|
||||
Les valeurs initiales sont insérées lors de la création de la base.
|
||||
|
||||
---
|
||||
|
||||
## Tables de liaison
|
||||
|
||||
Les relations suivantes doivent être prévues :
|
||||
|
||||
```text
|
||||
preuve_tags
|
||||
entite_tags
|
||||
personne_entites
|
||||
```
|
||||
|
||||
Le schéma existant contient également une table générique :
|
||||
|
||||
```text
|
||||
associations
|
||||
```
|
||||
|
||||
Cette table doit être auditée avant conservation.
|
||||
|
||||
Elle ne doit être gardée que si son rôle est clairement défini et ne fait pas
|
||||
doublon avec des relations spécialisées.
|
||||
|
||||
---
|
||||
|
||||
## Audit obligatoire
|
||||
|
||||
Avant d'écrire le schéma définitif, chaque table de la base historique doit
|
||||
être classée dans l'une des catégories suivantes :
|
||||
|
||||
```text
|
||||
CONSERVER
|
||||
MODIFIER
|
||||
RENOMMER
|
||||
FUSIONNER
|
||||
SUPPRIMER
|
||||
```
|
||||
|
||||
La décision doit être documentée dans :
|
||||
|
||||
```text
|
||||
docs/database/SCHEMA_AUDIT_V1.md
|
||||
```
|
||||
|
||||
Le document doit contenir au minimum :
|
||||
|
||||
```text
|
||||
Nom historique
|
||||
Décision
|
||||
Nom final
|
||||
Justification
|
||||
Modifications prévues
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Organisation du module Database
|
||||
|
||||
Le code SQL est organisé par domaine métier.
|
||||
|
||||
```
|
||||
include/
|
||||
└── database/
|
||||
├── database.h
|
||||
├── schema.h
|
||||
├── preuve.h
|
||||
├── entite.h
|
||||
├── personne.h
|
||||
├── source.h
|
||||
├── chronologie.h
|
||||
├── recherche.h
|
||||
├── hypothese.h
|
||||
├── journal.h
|
||||
├── tag.h
|
||||
├── type_preuve.h
|
||||
├── type_entite.h
|
||||
├── type_source.h
|
||||
└── type_outil.h
|
||||
|
||||
src/
|
||||
└── database/
|
||||
├── database.c
|
||||
├── schema.c
|
||||
├── preuve.c
|
||||
├── entite.c
|
||||
├── personne.c
|
||||
├── source.c
|
||||
├── chronologie.c
|
||||
├── recherche.c
|
||||
├── hypothese.c
|
||||
├── journal.c
|
||||
├── tag.c
|
||||
├── type_preuve.c
|
||||
├── type_entite.c
|
||||
├── type_source.c
|
||||
└── type_outil.c
|
||||
|
||||
database/
|
||||
├── schema_v1.sql
|
||||
├── indexes.sql
|
||||
├── triggers.sql
|
||||
└── reference_data.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Responsabilités
|
||||
|
||||
`database.c`
|
||||
|
||||
- ouverture de SQLite ;
|
||||
- fermeture ;
|
||||
- transactions ;
|
||||
- erreurs ;
|
||||
- point d'entrée du module Database.
|
||||
|
||||
`schema.c`
|
||||
|
||||
- installation du schéma ;
|
||||
- migrations ;
|
||||
- exécution des scripts SQL.
|
||||
|
||||
Chaque module métier est responsable exclusivement de son objet métier.
|
||||
|
||||
Exemple :
|
||||
|
||||
```
|
||||
preuve.c
|
||||
```
|
||||
|
||||
gère uniquement :
|
||||
|
||||
- création ;
|
||||
- lecture ;
|
||||
- modification ;
|
||||
- suppression logique ;
|
||||
- gestion des tags de la preuve.
|
||||
|
||||
Les tables de liaison simples ne possèdent pas de module dédié.
|
||||
|
||||
Exemples :
|
||||
|
||||
```
|
||||
preuve_tags
|
||||
personne_entites
|
||||
entite_tags
|
||||
```
|
||||
|
||||
Leur manipulation est réalisée par le module métier correspondant.
|
||||
|
||||
---
|
||||
|
||||
## Intégration dans `Database`
|
||||
|
||||
Le module `database_initialize()` doit exécuter le schéma officiel contenu
|
||||
dans :
|
||||
|
||||
```text
|
||||
database/schema_v1.sql
|
||||
```
|
||||
|
||||
Deux approches sont acceptables :
|
||||
|
||||
1. intégrer le schéma au binaire via une ressource GLib ;
|
||||
2. générer une chaîne C depuis le fichier SQL lors de la compilation.
|
||||
|
||||
Le logiciel installé ne doit pas dépendre d'un chemin relatif fragile vers le
|
||||
dépôt source.
|
||||
|
||||
---
|
||||
|
||||
## Principe d'organisation
|
||||
|
||||
Une table métier importante implique systématiquement :
|
||||
|
||||
- une structure C ;
|
||||
- un module C ;
|
||||
- des tests ;
|
||||
- une documentation.
|
||||
|
||||
La correspondance est la suivante :
|
||||
|
||||
| SQL | C |
|
||||
|-----|---|
|
||||
| preuves | preuve.c |
|
||||
| entites | entite.c |
|
||||
| personnes | personne.c |
|
||||
| sources | source.c |
|
||||
| chronologie | chronologie.c |
|
||||
| journal | journal.c |
|
||||
| recherches | recherche.c |
|
||||
| hypotheses | hypothese.c |
|
||||
|
||||
---
|
||||
|
||||
## Version du schéma
|
||||
|
||||
Le numéro de version reste :
|
||||
|
||||
```text
|
||||
1
|
||||
```
|
||||
|
||||
tant que Labfy Investigation n'a pas produit de base officiellement publiée.
|
||||
|
||||
Une fois le schéma V1 considéré comme stable et distribué, toute modification
|
||||
incompatible nécessitera :
|
||||
|
||||
- une nouvelle version ;
|
||||
- une migration dédiée ;
|
||||
- une mise à jour de `schema_version`.
|
||||
|
||||
---
|
||||
|
||||
## Hors périmètre
|
||||
|
||||
Ce ticket ne doit pas :
|
||||
|
||||
- développer les fonctions CRUD ;
|
||||
- créer les structures C `Preuve`, `Entite` ou `Personne` ;
|
||||
- connecter les tables à GTK ;
|
||||
- importer les anciennes données automatiquement ;
|
||||
- créer les migrations V2 ;
|
||||
- produire un rapport ;
|
||||
- ajouter la recherche plein texte.
|
||||
|
||||
---
|
||||
|
||||
## Contraintes techniques
|
||||
|
||||
- C17 ;
|
||||
- SQLite3 ;
|
||||
- GLib ;
|
||||
- aucune dépendance GTK ;
|
||||
- toutes les tables utilisent des contraintes explicites ;
|
||||
- toutes les clés étrangères sont documentées ;
|
||||
- toutes les tables de liaison empêchent les doublons ;
|
||||
- aucun SQL métier hors du module `Database` ;
|
||||
- aucun identifiant généré par SQLite pour les objets métier en UUID ;
|
||||
- compilation sans warning.
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
Faire évoluer :
|
||||
|
||||
```text
|
||||
tests/test_database.c
|
||||
```
|
||||
|
||||
Les tests doivent vérifier :
|
||||
|
||||
- la présence de toutes les tables officielles ;
|
||||
- la présence des tables de référence ;
|
||||
- l'insertion des valeurs initiales ;
|
||||
- l'activation des clés étrangères ;
|
||||
- le refus d'une clé étrangère invalide ;
|
||||
- le refus d'un doublon dans une table de liaison ;
|
||||
- le refus des colonnes obligatoires manquantes ;
|
||||
- l'unicité des codes des tables de référence ;
|
||||
- la valeur `schema_version = 1` ;
|
||||
- l'intégrité SQLite avec :
|
||||
|
||||
```sql
|
||||
PRAGMA integrity_check;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Critères d'acceptation
|
||||
|
||||
- [ ] Le schéma historique a été audité.
|
||||
- [ ] Chaque table possède une décision documentée.
|
||||
- [ ] Un schéma V1 officiel existe.
|
||||
- [ ] Les conventions de nommage sont uniformes.
|
||||
- [ ] Toutes les clés primaires sont explicites.
|
||||
- [ ] Toutes les clés étrangères sont explicites.
|
||||
- [ ] Les relations empêchent les doublons.
|
||||
- [ ] Les colonnes obligatoires utilisent `NOT NULL`.
|
||||
- [ ] Les tables de référence sont initialisées.
|
||||
- [ ] `database_initialize()` installe le schéma complet.
|
||||
- [ ] `PRAGMA integrity_check` retourne `ok`.
|
||||
- [ ] Tous les tests restent valides.
|
||||
- [ ] Aucune dépendance GTK.
|
||||
|
||||
---
|
||||
|
||||
## Livrables
|
||||
|
||||
```text
|
||||
docs/database/SCHEMA_AUDIT_V1.md
|
||||
database/schema_v1.sql
|
||||
include/database/database.h
|
||||
src/database/database.c
|
||||
tests/test_database.c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit attendu
|
||||
|
||||
```text
|
||||
feat(database): consolidate initial investigation schema
|
||||
```
|
||||
|
|
@ -11,19 +11,10 @@
|
|||
/**
|
||||
* @brief Initialise la base SQLite d'une enquête.
|
||||
*
|
||||
* La fonction crée ou ouvre le fichier SQLite indiqué, puis initialise
|
||||
* transactionnellement le schéma minimal de l'enquête.
|
||||
* La fonction crée ou ouvre le fichier SQLite indiqué, installe le schéma
|
||||
* courant, puis enregistre les métadonnées de l'enquête.
|
||||
*
|
||||
* Les informations suivantes sont enregistrées :
|
||||
*
|
||||
* - version du schéma ;
|
||||
* - nom de l'application ;
|
||||
* - date de création UTC ;
|
||||
* - UUID de l'enquête ;
|
||||
* - nom de l'enquête ;
|
||||
* - chemin racine de l'enquête.
|
||||
*
|
||||
* Aucun handle sqlite3 n'est exposé au code appelant.
|
||||
* Aucun handle SQLite n'est exposé au code appelant.
|
||||
*
|
||||
* @param database_path
|
||||
* Chemin complet du fichier Enquete.sqlite.
|
||||
|
|
|
|||
34
include/database/schema.h
Normal file
34
include/database/schema.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/******************************************************************************
|
||||
* @file schema.h
|
||||
* @brief Interface interne d'installation du schéma SQLite.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_SCHEMA_H
|
||||
#define LABFY_INVESTIGATION_SCHEMA_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
/**
|
||||
* @brief Installe le schéma SQLite V1 dans une base ouverte.
|
||||
*
|
||||
* La connexion doit être valide et une transaction doit déjà être active.
|
||||
*
|
||||
* Cette fonction crée :
|
||||
*
|
||||
* - les tables ;
|
||||
* - les index ;
|
||||
* - les valeurs de référence.
|
||||
*
|
||||
* Elle ne réalise ni COMMIT ni ROLLBACK.
|
||||
*
|
||||
* @param database Connexion SQLite ouverte.
|
||||
*
|
||||
* @return true si le schéma a été correctement installé, sinon false.
|
||||
*/
|
||||
bool schema_install_v1(
|
||||
sqlite3 *database
|
||||
);
|
||||
|
||||
#endif
|
||||
Binary file not shown.
8
resources/database.gresource.xml
Normal file
8
resources/database.gresource.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/com/labfytools/investigation/database">
|
||||
<file alias="schema_v1.sql">
|
||||
../database/schema_v1.sql
|
||||
</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
#include "database/database.h"
|
||||
#include "database/schema.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <sqlite3.h>
|
||||
|
|
@ -18,28 +19,6 @@
|
|||
*/
|
||||
#define DATABASE_APPLICATION_NAME "Labfy Investigation"
|
||||
|
||||
/**
|
||||
* @brief Crée la table contenant les métadonnées de l'enquête.
|
||||
*/
|
||||
static const char *const database_create_metadata_table_sql =
|
||||
"CREATE TABLE metadata"
|
||||
"("
|
||||
" key TEXT PRIMARY KEY,"
|
||||
" value TEXT NOT NULL"
|
||||
");";
|
||||
|
||||
/**
|
||||
* @brief Crée la table représentant l'enquête courante.
|
||||
*/
|
||||
static const char *const database_create_investigation_table_sql =
|
||||
"CREATE TABLE investigation"
|
||||
"("
|
||||
" id INTEGER PRIMARY KEY CHECK (id = 1),"
|
||||
" name TEXT NOT NULL,"
|
||||
" root_path TEXT NOT NULL,"
|
||||
" created_at TEXT NOT NULL"
|
||||
");";
|
||||
|
||||
/**
|
||||
* @brief Requête d'insertion d'une métadonnée.
|
||||
*/
|
||||
|
|
@ -55,11 +34,13 @@ static const char *const database_insert_investigation_sql =
|
|||
" id,"
|
||||
" name,"
|
||||
" root_path,"
|
||||
" created_at"
|
||||
" created_at,"
|
||||
" updated_at"
|
||||
")"
|
||||
"VALUES"
|
||||
"("
|
||||
" 1,"
|
||||
" ?,"
|
||||
" ?,"
|
||||
" ?,"
|
||||
" ?,"
|
||||
" ?"
|
||||
|
|
@ -319,6 +300,7 @@ static bool database_insert_all_metadata(
|
|||
*/
|
||||
static bool database_insert_investigation(
|
||||
sqlite3 *database,
|
||||
const char *investigation_uuid,
|
||||
const char *investigation_name,
|
||||
const char *investigation_root_path,
|
||||
const char *created_at
|
||||
|
|
@ -329,6 +311,7 @@ static bool database_insert_investigation(
|
|||
bool success = false;
|
||||
|
||||
if (database == NULL ||
|
||||
investigation_uuid == NULL ||
|
||||
investigation_name == NULL ||
|
||||
investigation_root_path == NULL ||
|
||||
created_at == NULL)
|
||||
|
|
@ -357,7 +340,7 @@ static bool database_insert_investigation(
|
|||
result = sqlite3_bind_text(
|
||||
statement,
|
||||
1,
|
||||
investigation_name,
|
||||
investigation_uuid,
|
||||
-1,
|
||||
SQLITE_TRANSIENT
|
||||
);
|
||||
|
|
@ -370,7 +353,7 @@ static bool database_insert_investigation(
|
|||
result = sqlite3_bind_text(
|
||||
statement,
|
||||
2,
|
||||
investigation_root_path,
|
||||
investigation_name,
|
||||
-1,
|
||||
SQLITE_TRANSIENT
|
||||
);
|
||||
|
|
@ -383,6 +366,32 @@ static bool database_insert_investigation(
|
|||
result = sqlite3_bind_text(
|
||||
statement,
|
||||
3,
|
||||
investigation_root_path,
|
||||
-1,
|
||||
SQLITE_TRANSIENT
|
||||
);
|
||||
|
||||
if (result != SQLITE_OK)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
result = sqlite3_bind_text(
|
||||
statement,
|
||||
4,
|
||||
created_at,
|
||||
-1,
|
||||
SQLITE_TRANSIENT
|
||||
);
|
||||
|
||||
if (result != SQLITE_OK)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
result = sqlite3_bind_text(
|
||||
statement,
|
||||
5,
|
||||
created_at,
|
||||
-1,
|
||||
SQLITE_TRANSIENT
|
||||
|
|
@ -530,22 +539,11 @@ bool database_initialize(
|
|||
|
||||
transaction_started = true;
|
||||
|
||||
if (!database_execute_sql(
|
||||
database,
|
||||
database_create_metadata_table_sql
|
||||
))
|
||||
if (!schema_install_v1(database))
|
||||
{
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
if (!database_execute_sql(
|
||||
database,
|
||||
database_create_investigation_table_sql
|
||||
))
|
||||
{
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
|
||||
if (!database_insert_all_metadata(
|
||||
database,
|
||||
created_at,
|
||||
|
|
@ -557,6 +555,7 @@ bool database_initialize(
|
|||
|
||||
if (!database_insert_investigation(
|
||||
database,
|
||||
investigation_uuid,
|
||||
investigation_name,
|
||||
investigation_root_path,
|
||||
created_at
|
||||
|
|
|
|||
91
src/database/schema.c
Normal file
91
src/database/schema.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/******************************************************************************
|
||||
* @file schema.c
|
||||
* @brief Installation du schéma SQLite de Labfy Investigation.
|
||||
******************************************************************************/
|
||||
|
||||
#include "database/schema.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* @brief Charge le schéma SQL V1 depuis les ressources intégrées.
|
||||
*
|
||||
* @return Une nouvelle chaîne terminée par zéro, à libérer avec g_free(),
|
||||
* ou NULL en cas d'échec.
|
||||
*/
|
||||
static char *schema_load_v1_sql(void)
|
||||
{
|
||||
char *schema_sql = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!g_file_get_contents(
|
||||
"database/schema_v1.sql",
|
||||
&schema_sql,
|
||||
NULL,
|
||||
&error
|
||||
))
|
||||
{
|
||||
g_warning(
|
||||
"Impossible de charger database/schema_v1.sql : %s",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "erreur inconnue"
|
||||
);
|
||||
|
||||
g_clear_error(&error);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return schema_sql;
|
||||
}
|
||||
|
||||
bool schema_install_v1(
|
||||
sqlite3 *database
|
||||
)
|
||||
{
|
||||
char *schema_sql = NULL;
|
||||
char *error_message = NULL;
|
||||
|
||||
int result = SQLITE_ERROR;
|
||||
|
||||
if (database == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
schema_sql = schema_load_v1_sql();
|
||||
|
||||
if (schema_sql == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = sqlite3_exec(
|
||||
database,
|
||||
schema_sql,
|
||||
NULL,
|
||||
NULL,
|
||||
&error_message
|
||||
);
|
||||
|
||||
g_free(schema_sql);
|
||||
|
||||
if (result != SQLITE_OK)
|
||||
{
|
||||
g_warning(
|
||||
"Impossible d'installer le schéma SQLite V1 : %s",
|
||||
error_message != NULL
|
||||
? error_message
|
||||
: sqlite3_errmsg(database)
|
||||
);
|
||||
|
||||
sqlite3_free(error_message);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_free(error_message);
|
||||
|
||||
return true;
|
||||
}
|
||||
Binary file not shown.
|
|
@ -131,6 +131,8 @@ static void test_database_initialize_valid_database(void)
|
|||
char *investigation_name = NULL;
|
||||
char *investigation_root_path = NULL;
|
||||
char *investigation_created_at = NULL;
|
||||
char *investigation_id = NULL;
|
||||
char *investigation_count = NULL;
|
||||
|
||||
sqlite3 *database = NULL;
|
||||
GError *error = NULL;
|
||||
|
|
@ -214,19 +216,31 @@ static void test_database_initialize_valid_database(void)
|
|||
investigation_name = test_database_read_single_text(
|
||||
database,
|
||||
"SELECT name FROM investigation "
|
||||
"WHERE id = 1;"
|
||||
"LIMIT 1;"
|
||||
);
|
||||
|
||||
investigation_root_path = test_database_read_single_text(
|
||||
database,
|
||||
"SELECT root_path FROM investigation "
|
||||
"WHERE id = 1;"
|
||||
"LIMIT 1;"
|
||||
);
|
||||
|
||||
investigation_created_at = test_database_read_single_text(
|
||||
database,
|
||||
"SELECT created_at FROM investigation "
|
||||
"WHERE id = 1;"
|
||||
"LIMIT 1;"
|
||||
);
|
||||
|
||||
investigation_id = test_database_read_single_text(
|
||||
database,
|
||||
"SELECT id FROM investigation "
|
||||
"LIMIT 1;"
|
||||
);
|
||||
|
||||
investigation_count = test_database_read_single_text(
|
||||
database,
|
||||
"SELECT CAST(COUNT(*) AS TEXT) "
|
||||
"FROM investigation;"
|
||||
);
|
||||
|
||||
assert(strcmp(schema_version, "1") == 0);
|
||||
|
|
@ -246,6 +260,11 @@ static void test_database_initialize_valid_database(void)
|
|||
assert(g_remove(database_path) == 0);
|
||||
assert(g_rmdir(temporary_directory) == 0);
|
||||
|
||||
assert(investigation_id[0] != '\0');
|
||||
assert(g_uuid_string_is_valid(investigation_id));
|
||||
|
||||
assert(strcmp(investigation_count, "1") == 0);
|
||||
|
||||
g_free(investigation_created_at);
|
||||
g_free(investigation_root_path);
|
||||
g_free(investigation_name);
|
||||
|
|
@ -255,6 +274,8 @@ static void test_database_initialize_valid_database(void)
|
|||
g_free(schema_version);
|
||||
g_free(database_path);
|
||||
g_free(temporary_directory);
|
||||
g_free(investigation_id);
|
||||
g_free(investigation_count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in a new issue