judu/ex-egopublic Fork
⑂ 8c08a86
Commits
⬇ Clone ▾
git clone https://git.rickub.com/judu/ex-ego.git
git clone ssh://git@rickub.com/judu/ex-ego.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

feat(db): les énumérations deviennent des tables, dans un schéma dédiéUnverified

Les domaines fermés (langue, thème, format des stats) passaient par un
CHECK IN : les élargir demandait un DDL qui réécrit la contrainte. Ils
deviennent des tables exego_enums.enum_xxx référencées par clé étrangère,
où ajouter une valeur est un INSERT.

Les colonnes métier gardent leur TEXT et leur valeur — la clé primaire de
la table enum EST le code — donc aucune requête ne change. Le schéma dédié
tient ces tables hors du scope applicatif : seules les FK y renvoient.

todo_states, qui appliquait déjà le principe depuis public, rejoint le
schéma sous le nom enum_todo_state ; ses deux FK entrantes suivent le
déplacement. minutes > 0 reste un CHECK : ce n'est pas une énumération.
Julien Durillon committed 2026-08-19T10:20:51+02:00 Browse files
8c08a86 parent: 906353d
added migrations/0013_enum_tables.sql +84 -0
new file mode 100644
@@ -0,0 +1,84 @@
1+-- Principe : une valeur d'énumération n'est pas un CHECK, c'est une ligne.
2+-- Chaque domaine fermé devient une table `enum_xxx` référencée par une clé
3+-- étrangère — élargir une énumération est alors un INSERT (donc réversible,
4+-- inspectable, joignable) au lieu d'un DDL qui réécrit une contrainte.
5+--
6+-- Ces tables vivent dans le schéma `exego_enums` pour ne pas encombrer le
7+-- scope applicatif : aucune requête de l'app ne les nomme, seules les clés
8+-- étrangères y renvoient (l'intégrité référentielle ignore le search_path).
9+CREATE SCHEMA IF NOT EXISTS exego_enums;
10+
11+COMMENT ON SCHEMA exego_enums IS
12+ 'Tables de référence des énumérations (une ligne par valeur permise).';
13+
14+-- Colonnes communes : `code` est la valeur stockée dans les tables métier,
15+-- `sort_order` l'ordre d'affichage, `description` un libellé de lecture
16+-- humaine EN BASE — jamais du texte d'interface (celui-ci reste dans i18n.rs).
17+
18+-- users.lang -----------------------------------------------------------------
19+CREATE TABLE exego_enums.enum_lang (
20+ code TEXT PRIMARY KEY,
21+ sort_order SMALLINT NOT NULL,
22+ description TEXT NOT NULL
23+);
24+INSERT INTO exego_enums.enum_lang (code, sort_order, description) VALUES
25+ ('fr', 1, 'français'),
26+ ('en', 2, 'anglais'),
27+ ('de', 3, 'allemand'),
28+ ('pt-br', 4, 'portugais du Brésil');
29+
30+ALTER TABLE users DROP CONSTRAINT IF EXISTS users_lang_check;
31+ALTER TABLE users ADD CONSTRAINT users_lang_fkey
32+ FOREIGN KEY (lang) REFERENCES exego_enums.enum_lang(code);
33+
34+-- users.theme ----------------------------------------------------------------
35+CREATE TABLE exego_enums.enum_theme (
36+ code TEXT PRIMARY KEY,
37+ sort_order SMALLINT NOT NULL,
38+ description TEXT NOT NULL
39+);
40+INSERT INTO exego_enums.enum_theme (code, sort_order, description) VALUES
41+ ('organic', 1, 'crème et terracotta (défaut)'),
42+ ('hc', 2, 'contraste élevé noir et blanc');
43+
44+ALTER TABLE users DROP CONSTRAINT IF EXISTS users_theme_check;
45+ALTER TABLE users ADD CONSTRAINT users_theme_fkey
46+ FOREIGN KEY (theme) REFERENCES exego_enums.enum_theme(code);
47+
48+-- users.stats_layout ---------------------------------------------------------
49+CREATE TABLE exego_enums.enum_stats_layout (
50+ code TEXT PRIMARY KEY,
51+ sort_order SMALLINT NOT NULL,
52+ description TEXT NOT NULL
53+);
54+INSERT INTO exego_enums.enum_stats_layout (code, sort_order, description) VALUES
55+ ('columns', 1, 'colonnes empilées (défaut)'),
56+ ('rows', 2, 'barres horizontales');
57+
58+ALTER TABLE users DROP CONSTRAINT IF EXISTS users_stats_layout_check;
59+ALTER TABLE users ADD CONSTRAINT users_stats_layout_fkey
60+ FOREIGN KEY (stats_layout) REFERENCES exego_enums.enum_stats_layout(code);
61+
62+-- todos.state_id -------------------------------------------------------------
63+-- `todo_states` appliquait déjà le principe, mais depuis le schéma public.
64+-- Le déplacement emporte ses clés étrangères (todos, todo_state_changes) :
65+-- rien à recréer. La clé reste le SMALLINT, que le Rust connaît par les
66+-- constantes STATE_* de src/db/todos.rs.
67+ALTER TABLE todo_states SET SCHEMA exego_enums;
68+ALTER TABLE exego_enums.todo_states RENAME TO enum_todo_state;
69+ALTER TABLE exego_enums.enum_todo_state RENAME COLUMN state_order TO sort_order;
70+ALTER TABLE exego_enums.enum_todo_state RENAME COLUMN state_name TO code;
71+-- Les noms de contraintes sont cherchés, pas écrits en dur (leçon de la 0012).
72+DO $$
73+DECLARE c text;
74+BEGIN
75+ SELECT conname INTO c FROM pg_constraint
76+ WHERE conrelid = 'exego_enums.enum_todo_state'::regclass AND contype = 'p';
77+ EXECUTE format('ALTER TABLE exego_enums.enum_todo_state RENAME CONSTRAINT %I TO enum_todo_state_pkey', c);
78+
79+ SELECT conname INTO c FROM pg_constraint
80+ WHERE conrelid = 'exego_enums.enum_todo_state'::regclass AND contype = 'u';
81+ IF c IS NOT NULL THEN
82+ EXECUTE format('ALTER TABLE exego_enums.enum_todo_state RENAME CONSTRAINT %I TO enum_todo_state_code_key', c);
83+ END IF;
84+END $$;
new file mode 100644
@@ -0,0 +1,84 @@
1+-- Principe : une valeur d'énumération n'est pas un CHECK, c'est une ligne.
2+-- Chaque domaine fermé devient une table `enum_xxx` référencée par une clé
3+-- étrangère — élargir une énumération est alors un INSERT (donc réversible,
4+-- inspectable, joignable) au lieu d'un DDL qui réécrit une contrainte.
5+--
6+-- Ces tables vivent dans le schéma `exego_enums` pour ne pas encombrer le
7+-- scope applicatif : aucune requête de l'app ne les nomme, seules les clés
8+-- étrangères y renvoient (l'intégrité référentielle ignore le search_path).
9+CREATE SCHEMA IF NOT EXISTS exego_enums;
10+
11+COMMENT ON SCHEMA exego_enums IS
12+ 'Tables de référence des énumérations (une ligne par valeur permise).';
13+
14+-- Colonnes communes : `code` est la valeur stockée dans les tables métier,
15+-- `sort_order` l'ordre d'affichage, `description` un libellé de lecture
16+-- humaine EN BASE — jamais du texte d'interface (celui-ci reste dans i18n.rs).
17+
18+-- users.lang -----------------------------------------------------------------
19+CREATE TABLE exego_enums.enum_lang (
20+ code TEXT PRIMARY KEY,
21+ sort_order SMALLINT NOT NULL,
22+ description TEXT NOT NULL
23+);
24+INSERT INTO exego_enums.enum_lang (code, sort_order, description) VALUES
25+ ('fr', 1, 'français'),
26+ ('en', 2, 'anglais'),
27+ ('de', 3, 'allemand'),
28+ ('pt-br', 4, 'portugais du Brésil');
29+
30+ALTER TABLE users DROP CONSTRAINT IF EXISTS users_lang_check;
31+ALTER TABLE users ADD CONSTRAINT users_lang_fkey
32+ FOREIGN KEY (lang) REFERENCES exego_enums.enum_lang(code);
33+
34+-- users.theme ----------------------------------------------------------------
35+CREATE TABLE exego_enums.enum_theme (
36+ code TEXT PRIMARY KEY,
37+ sort_order SMALLINT NOT NULL,
38+ description TEXT NOT NULL
39+);
40+INSERT INTO exego_enums.enum_theme (code, sort_order, description) VALUES
41+ ('organic', 1, 'crème et terracotta (défaut)'),
42+ ('hc', 2, 'contraste élevé noir et blanc');
43+
44+ALTER TABLE users DROP CONSTRAINT IF EXISTS users_theme_check;
45+ALTER TABLE users ADD CONSTRAINT users_theme_fkey
46+ FOREIGN KEY (theme) REFERENCES exego_enums.enum_theme(code);
47+
48+-- users.stats_layout ---------------------------------------------------------
49+CREATE TABLE exego_enums.enum_stats_layout (
50+ code TEXT PRIMARY KEY,
51+ sort_order SMALLINT NOT NULL,
52+ description TEXT NOT NULL
53+);
54+INSERT INTO exego_enums.enum_stats_layout (code, sort_order, description) VALUES
55+ ('columns', 1, 'colonnes empilées (défaut)'),
56+ ('rows', 2, 'barres horizontales');
57+
58+ALTER TABLE users DROP CONSTRAINT IF EXISTS users_stats_layout_check;
59+ALTER TABLE users ADD CONSTRAINT users_stats_layout_fkey
60+ FOREIGN KEY (stats_layout) REFERENCES exego_enums.enum_stats_layout(code);
61+
62+-- todos.state_id -------------------------------------------------------------
63+-- `todo_states` appliquait déjà le principe, mais depuis le schéma public.
64+-- Le déplacement emporte ses clés étrangères (todos, todo_state_changes) :
65+-- rien à recréer. La clé reste le SMALLINT, que le Rust connaît par les
66+-- constantes STATE_* de src/db/todos.rs.
67+ALTER TABLE todo_states SET SCHEMA exego_enums;
68+ALTER TABLE exego_enums.todo_states RENAME TO enum_todo_state;
69+ALTER TABLE exego_enums.enum_todo_state RENAME COLUMN state_order TO sort_order;
70+ALTER TABLE exego_enums.enum_todo_state RENAME COLUMN state_name TO code;
71+-- Les noms de contraintes sont cherchés, pas écrits en dur (leçon de la 0012).
72+DO $$
73+DECLARE c text;
74+BEGIN
75+ SELECT conname INTO c FROM pg_constraint
76+ WHERE conrelid = 'exego_enums.enum_todo_state'::regclass AND contype = 'p';
77+ EXECUTE format('ALTER TABLE exego_enums.enum_todo_state RENAME CONSTRAINT %I TO enum_todo_state_pkey', c);
78+
79+ SELECT conname INTO c FROM pg_constraint
80+ WHERE conrelid = 'exego_enums.enum_todo_state'::regclass AND contype = 'u';
81+ IF c IS NOT NULL THEN
82+ EXECUTE format('ALTER TABLE exego_enums.enum_todo_state RENAME CONSTRAINT %I TO enum_todo_state_code_key', c);
83+ END IF;
84+END $$;
modified src/db/todos.rs +2 -1
@@ -2,7 +2,8 @@ use chrono::{DateTime, NaiveDate, Utc};
22 use sqlx::PgPool;
33 use uuid::Uuid;
44
5-/// États — mêmes ids que la table enum `todo_states` (seedée en migration).
5+/// États — mêmes ids que la table enum `exego_enums.enum_todo_state`
6+/// (seedée en migration).
67 pub const STATE_IN_PROGRESS: i16 = 1;
78 pub const STATE_TODO: i16 = 2;
89 pub const STATE_DONE: i16 = 3;
@@ -2,7 +2,8 @@ use chrono::{DateTime, NaiveDate, Utc};
2 use sqlx::PgPool;2 use sqlx::PgPool;
3 use uuid::Uuid;3 use uuid::Uuid;
4 4
5-/// États — mêmes ids que la table enum `todo_states` (seedée en migration).5+/// États — mêmes ids que la table enum `exego_enums.enum_todo_state`
6+/// (seedée en migration).
6 pub const STATE_IN_PROGRESS: i16 = 1;7 pub const STATE_IN_PROGRESS: i16 = 1;
7 pub const STATE_TODO: i16 = 2;8 pub const STATE_TODO: i16 = 2;
8 pub const STATE_DONE: i16 = 3;9 pub const STATE_DONE: i16 = 3;