Projet

Général

Profil

Anomalie #312 » a_executer.sql

Script de correction du mot de passe de contrôle - Leslie Lemaire, 27/08/2020 19:38

 
1

    
2
--------------------------------------------
3
------ 3 - CREATION DES EVENT TRIGGERS ------
4
--------------------------------------------
5

    
6
------ 3.1 - EVENT TRIGGER SUR ALTER SCHEMA ------
7

    
8
-- FUNCTION: z_asgard_admin.asgard_on_alter_schema()
9

    
10
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_alter_schema() RETURNS event_trigger
11
    LANGUAGE plpgsql
12
    AS $BODY$
13
/* OBJET : Fonction exécutée par l'event trigger asgard_on_alter_schema qui
14
           répercute dans la table z_asgard_admin.gestion_schema (via la vue
15
           z_asgard.gestion_schema_etr) les modifications de noms
16
           et propriétaires des schémas réalisées par des commandes
17
           ALTER SCHEMA directes.
18
DECLENCHEMENT : ON DDL COMMAND END.
19
CONDITION : WHEN TAG IN ('ALTER SCHEMA') */
20
DECLARE
21
    obj record ;
22
    e_mssg text ;
23
    e_hint text ;
24
    e_detl text ;
25
BEGIN
26
    ------ CONTROLES DES PRIVILEGES ------
27
    IF NOT has_schema_privilege('z_asgard', 'USAGE')
28
    THEN
29
        RAISE EXCEPTION 'EAS1. Echec.'
30
            USING HINT = 'Vous devez être membre du groupe éditeur du schéma z_asgard pour réaliser cette opération.' ;
31
    END IF ;
32
    
33
    IF NOT has_table_privilege('z_asgard.gestion_schema_etr', 'UPDATE')
34
             OR NOT has_table_privilege('z_asgard.gestion_schema_etr', 'SELECT')
35
    THEN
36
        RAISE EXCEPTION 'EAS2. Echec.'
37
            USING HINT = 'Vous devez être membre du groupe éditeur du schéma z_asgard pour réaliser cette opération.' ;
38
    END IF ;
39

    
40

    
41
	FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
42
                    WHERE object_type = 'schema'
43
    LOOP
44
    
45
        ------ RENAME ------
46
        UPDATE z_asgard.gestion_schema_etr
47
            SET nom_schema = replace(obj.object_identity, '"', ''),
48
                ctrl = ARRAY['RENAME', 'x7-A;#rzo']
49
            WHERE oid_schema = obj.objid
50
                AND NOT quote_ident(nom_schema) = obj.object_identity ;
51
        IF FOUND
52
        THEN
53
            RAISE NOTICE '... Le nom du schéma % a été mis à jour dans la table de gestion.',  replace(obj.object_identity, '"', '') ;
54
        END IF ;
55

    
56
        ------ OWNER TO ------
57
        UPDATE z_asgard.gestion_schema_etr
58
            SET (producteur, oid_producteur, ctrl) = (
59
                SELECT
60
                    replace(nspowner::regrole::text, '"', ''),
61
                    nspowner,
62
                    ARRAY['OWNER', 'x7-A;#rzo']
63
                    FROM pg_catalog.pg_namespace
64
                    WHERE obj.objid = pg_namespace.oid
65
                )
66
			WHERE oid_schema = obj.objid
67
    			AND NOT oid_producteur = (
68
             		SELECT nspowner
69
                  	FROM pg_catalog.pg_namespace
70
                 	 WHERE obj.objid = pg_namespace.oid
71
             		) ;
72
        IF FOUND
73
        THEN
74
            RAISE NOTICE '... Le producteur du schéma % a été mis à jour dans la table de gestion.',  replace(obj.object_identity, '"', '') ;
75
        END IF ;
76

    
77
    END LOOP ;
78
    
79
EXCEPTION WHEN OTHERS THEN
80
    GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT,
81
                            e_hint = PG_EXCEPTION_HINT,
82
                            e_detl = PG_EXCEPTION_DETAIL ;
83
    RAISE EXCEPTION 'EAS0. Opération annulée. Anomalie lors de l''enregistrement dans la table de gestion.'
84
         USING DETAIL = CASE WHEN e_detl = '' THEN e_mssg ELSE e_mssg || ' / ' ||  e_detl END,
85
               HINT = e_hint ;
86
               
87
END
88
$BODY$ ;
89

    
90

    
91
------ 3.2 - EVENT TRIGGER SUR CREATE SCHEMA ------
92

    
93
-- FUNCTION: z_asgard_admin.asgard_on_create_schema()
94

    
95
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_create_schema() RETURNS event_trigger
96
    LANGUAGE plpgsql
97
    AS $BODY$
98
/* OBJET : Fonction exécutée par l'event trigger asgard_on_create_schema qui
99
           répercute dans la table z_asgard_admin.gestion_schema (via la vue
100
           z_asgard.gestion_schema_etr) les créations de schémas
101
           réalisées par des commandes CREATE SCHEMA directes.
102
DECLENCHEMENT : ON DDL COMMAND END.
103
CONDITION : WHEN TAG IN ('CREATE SCHEMA') */
104
DECLARE
105
    obj record ;
106
    e_mssg text ;
107
    e_hint text ;
108
    e_detl text ;
109
BEGIN
110
    ------ CONTROLES DES PRIVILEGES ------
111
    IF NOT has_schema_privilege('z_asgard', 'USAGE')
112
    THEN
113
        RAISE EXCEPTION 'ECS1. Echec.'
114
            USING HINT = 'Vous devez être membre du groupe éditeur du schéma z_asgard pour réaliser cette opération.' ;
115
    END IF ;
116
    
117
    IF NOT has_table_privilege('z_asgard.gestion_schema_etr', 'UPDATE')
118
            OR NOT has_table_privilege('z_asgard.gestion_schema_etr', 'INSERT')
119
            OR NOT has_table_privilege('z_asgard.gestion_schema_etr', 'SELECT')
120
    THEN
121
        RAISE EXCEPTION 'ECS2. Echec.'
122
            USING HINT = 'Vous devez être membre du groupe éditeur du schéma z_asgard pour réaliser cette opération.' ;
123
    END IF ;
124

    
125

    
126
	FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
127
                    WHERE object_type = 'schema'
128
    LOOP
129
    
130
        ------ SCHEMA PRE-ENREGISTRE DANS GESTION_SCHEMA ------
131
        UPDATE z_asgard.gestion_schema_etr
132
            SET (oid_schema, producteur, oid_producteur, creation, ctrl) = (
133
                SELECT
134
                    obj.objid,
135
                    replace(nspowner::regrole::text, '"', ''),
136
                    nspowner,
137
                    true,
138
                    ARRAY['CREATE', 'x7-A;#rzo']
139
                    FROM pg_catalog.pg_namespace
140
                    WHERE obj.objid = pg_namespace.oid
141
                )
142
            WHERE quote_ident(nom_schema) = obj.object_identity
143
                AND NOT creation  ; -- creation vaut true si et seulement si la création a été initiée via la table
144
                                    -- de gestion dans ce cas, il n'est pas nécessaire de réintervenir dessus
145
        IF FOUND
146
        THEN
147
            RAISE NOTICE '... Le schéma % apparaît désormais comme "créé" dans la table de gestion.',  replace(obj.object_identity, '"', '') ;
148

    
149
        ------ SCHEMA NON REPERTORIE DANS GESTION_SCHEMA ------
150
        ELSIF NOT obj.object_identity IN (SELECT quote_ident(nom_schema) FROM z_asgard.gestion_schema_etr)
151
        THEN
152
            INSERT INTO z_asgard.gestion_schema_etr (oid_schema, nom_schema, producteur, oid_producteur, creation, ctrl)(
153
                SELECT
154
                    obj.objid,
155
                    replace(obj.object_identity, '"', ''),
156
                    replace(nspowner::regrole::text, '"', ''),
157
                    nspowner,
158
                    true,
159
                    ARRAY['CREATE', 'x7-A;#rzo']
160
                    FROM pg_catalog.pg_namespace
161
                    WHERE obj.objid = pg_namespace.oid
162
                ) ;
163
            RAISE NOTICE '... Le schéma % a été enregistré dans la table de gestion.',  replace(obj.object_identity, '"', '') ;
164
        END IF ;
165
        
166
	END LOOP ;
167
    
168
EXCEPTION WHEN OTHERS THEN
169
    GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT,
170
                            e_hint = PG_EXCEPTION_HINT,
171
                            e_detl = PG_EXCEPTION_DETAIL ;
172
    RAISE EXCEPTION 'ECS0. Opération annulée. Anomalie lors de l''enregistrement dans la table de gestion.'
173
         USING DETAIL = CASE WHEN e_detl = '' THEN e_mssg ELSE e_mssg || ' / ' ||  e_detl END,
174
               HINT = e_hint ;
175
               
176
END
177
$BODY$ ;
178

    
179

    
180

    
181
------ 3.3 - EVENT TRIGGER SUR DROP SCHEMA ------
182

    
183
-- FUNCTION: z_asgard_admin.asgard_on_drop_schema()
184

    
185
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_drop_schema() RETURNS event_trigger
186
    LANGUAGE plpgsql
187
    AS $BODY$
188
/* OBJET : Fonction exécutée par l'event trigger asgard_on_drop_schema qui
189
           répercute dans la table z_asgard_admin.gestion_schema (via la vue
190
           z_asgard.gestion_schema_etr) les suppressions de schémas
191
           réalisées par des commandes DROP SCHEMA directes.
192
DECLENCHEMENT : ON SQL DROP.
193
CONDITION : WHEN TAG IN ('DROP SCHEMA') */
194
DECLARE
195
	obj record ;
196
    e_mssg text ;
197
    e_hint text ;
198
    e_detl text ;
199
BEGIN
200
    ------ CONTROLES DES PRIVILEGES ------
201
    IF NOT has_schema_privilege('z_asgard', 'USAGE')
202
    THEN
203
        RAISE EXCEPTION 'EDS1. Echec.'
204
            USING HINT = 'Vous devez être membre du groupe éditeur du schéma z_asgard pour réaliser cette opération.' ;
205
    END IF ;
206
    
207
    IF NOT has_table_privilege('z_asgard.gestion_schema_etr', 'UPDATE')
208
            OR NOT has_table_privilege('z_asgard.gestion_schema_etr', 'SELECT')
209
    THEN
210
        RAISE EXCEPTION 'EDS2. Echec.'
211
            USING HINT = 'Vous devez être membre du groupe éditeur du schéma z_asgard pour réaliser cette opération.' ;
212
    END IF ;
213
    
214

    
215
	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
216
                    WHERE object_type = 'schema'
217
    LOOP
218
        ------ ENREGISTREMENT DE LA SUPPRESSION ------
219
		UPDATE z_asgard.gestion_schema_etr
220
			SET (creation, oid_schema, ctrl) = (False, NULL, ARRAY['DROP', 'x7-A;#rzo'])
221
			WHERE quote_ident(nom_schema) = obj.object_identity ;    
222
		IF FOUND THEN
223
			RAISE NOTICE '... La suppression du schéma % a été enregistrée dans la table de gestion (creation = False).', replace(obj.object_identity, '"', '');
224
		END IF ;
225
        
226
	END LOOP ;
227
    
228
EXCEPTION WHEN OTHERS THEN
229
    GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT,
230
                            e_hint = PG_EXCEPTION_HINT,
231
                            e_detl = PG_EXCEPTION_DETAIL ;
232
    RAISE EXCEPTION 'EDS0. Opération annulée. Anomalie lors de l''enregistrement dans la table de gestion.'
233
         USING DETAIL = CASE WHEN e_detl = '' THEN e_mssg ELSE e_mssg || ' / ' ||  e_detl END,
234
               HINT = e_hint ;
235
               
236
END
237
$BODY$;
238

    
239

    
240

    
241
---------------------------------------
242
------ 4 - FONCTIONS UTILITAIRES ------
243
---------------------------------------
244

    
245

    
246
------ 4.6 - DEREFERENCEMENT D'UN SCHEMA ------
247

    
248
-- FUNCTION: z_asgard_admin.asgard_sortie_gestion_schema(text)
249

    
250
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_sortie_gestion_schema(n_schema text)
251
    RETURNS text
252
    LANGUAGE plpgsql
253
    AS $_$
254
/* OBJET : Cette fonction permet de supprimer de la table de gestion un
255
           schéma existant (qui échappera alors aux mécanismes de
256
           gestion des droits), en outrepassant les règles qui veulent
257
           que seules les lignes avec creation valant false puisse
258
           être ciblées par des DELETE et que creation ne puisse être
259
           mis à false si le schéma existe.
260
ARGUMENTS :
261
- n_schema : nom d'un schéma présumé référencé dans le champ
262
             nom_schema de la table de gestion (sinon la fonction n'aura
263
             pas d'effet).
264
SORTIE : '__ DEREFERENCEMENT REUSSI.' si la requête s'est exécutée normalement. */
265
DECLARE
266
    e_mssg text ;
267
    e_detl text ;
268
    e_hint text ;
269
BEGIN
270

    
271
    UPDATE z_asgard.gestion_schema_etr
272
        SET ctrl = ARRAY['EXIT', 'x7-A;#rzo']
273
        WHERE nom_schema = n_schema ;
274
        
275
    DELETE FROM z_asgard.gestion_schema_etr
276
        WHERE nom_schema = n_schema ;
277

    
278
    RETURN '__ DEREFERENCEMENT REUSSI.' ;
279

    
280
EXCEPTION WHEN OTHERS THEN
281
    GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT,
282
                            e_hint = PG_EXCEPTION_HINT,
283
                            e_detl = PG_EXCEPTION_DETAIL ;
284
    RAISE EXCEPTION 'FSG0. Opération annulée. Anomalie lors du déréférencement de la table de gestion.'
285
         USING DETAIL = CASE WHEN e_detl = '' THEN e_mssg ELSE e_mssg || ' / ' ||  e_detl END,
286
               HINT = e_hint ;
287

    
288
END
289
$_$;
290

    
291

    
292

    
293

    
294
---------------------------------------------
295
------ 5 - TRIGGERS SUR GESTION_SCHEMA ------
296
---------------------------------------------
297

    
298
------ 5.1 - TRIGGER BEFORE ------
299

    
300
-- FUNCTION: z_asgard_admin.asgard_on_modify_gestion_schema_before()
301

    
302
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_before() RETURNS trigger
303
    LANGUAGE plpgsql
304
    AS $BODY$
305
/* OBJET : Fonction exécutée par le trigger asgard_on_modify_gestion_schema_before,
306
           qui valide les informations saisies dans la table de gestion.
307
CIBLES : z_asgard_admin.gestion_schema.
308
PORTEE : FOR EACH ROW.
309
DECLENCHEMENT : BEFORE INSERT, UPDATE, DELETE.*/
310
DECLARE
311
    n_role text ;
312
BEGIN
313
    
314
    ------ SAISIE PAR UN UTILISATEUR NON HABILITE ------
315
    IF TG_OP = 'INSERT' AND NOT has_database_privilege(current_database(), 'CREATE')
316
    -- même si creation vaut faux, seul un rôle habilité à créer des
317
    -- schéma peut ajouter des lignes dans la table de gestion
318
    THEN
319
        RAISE EXCEPTION 'TB1. Vous devez être habilité à créer des schémas pour réaliser cette opération.' ;
320
    END IF ;
321
    
322
    ------ APPLICATION DES VALEURS PAR DEFAUT ------
323
    -- au tout début car de nombreux tests sont faits par la
324
    -- suite sur "NOT NEW.creation"
325
    IF TG_OP IN ('INSERT', 'UPDATE')
326
    THEN
327
        NEW.creation := coalesce(NEW.creation, False) ;
328
        NEW.nomenclature := coalesce(NEW.nomenclature, False) ;
329
    END IF ;
330
    
331
    ------ EFFACEMENT D'UN ENREGISTREMENT ------
332
    IF TG_OP = 'DELETE'
333
    THEN   
334
        -- on n'autorise pas l'effacement si creation vaut True
335
        -- avec une exception pour les commandes envoyées par la fonction
336
        -- de maintenance asgard_sortie_gestion_schema
337
        IF OLD.creation AND (OLD.ctrl[1] IS NULL OR NOT OLD.ctrl[1] = 'EXIT')
338
        THEN
339
            RAISE EXCEPTION 'TB2. Opération interdite (schéma %). L''effacement n''est autorisé que si creation vaut False.', OLD.nom_schema
340
                USING HINT = 'Pour déréférencer un schéma sans le supprimer, vous pouvez utiliser la fonction z_asgard_admin.asgard_sortie_gestion_schema.' ;
341
        END IF;
342
        
343
        -- on n'autorise pas l'effacement pour les schémas de la nomenclature
344
        IF OLD.nomenclature
345
        THEN
346
            RAISE EXCEPTION 'TB3. Opération interdite (schéma %). L''effacement n''est pas autorisé pour les schémas de la nomenclature nationale.', OLD.nom_schema
347
                USING HINT = 'Si vous tenez à supprimer définitivement cet enregistrement, basculez préalablement nomenclature sur False.' ;
348
        END IF ;
349
    END IF;
350

    
351
    ------ DE-CREATION D'UN SCHEMA ------
352
    IF TG_OP = 'UPDATE'
353
    THEN
354
        -- si bloc valait déjà d (schéma "mis à la corbeille")
355
        -- on exécute une commande de suppression du schéma. Toute autre modification sur
356
        -- la ligne est ignorée.
357
        IF OLD.bloc = 'd' AND OLD.creation AND NOT NEW.creation AND NEW.ctrl[2] IS NULL
358
                AND OLD.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
359
        THEN
360
            EXECUTE 'DROP SCHEMA ' || quote_ident(OLD.nom_schema) || ' CASCADE' ;
361
            RAISE NOTICE '... Le schéma % a été supprimé.', OLD.nom_schema ;
362
            RETURN NULL ;
363
        -- sinon, on n'autorise creation à passer de true à false que si le schéma
364
        -- n'existe plus (permet notamment à l'event trigger qui gère les
365
        -- suppressions de mettre creation à false)
366
        ELSIF OLD.creation and NOT NEW.creation
367
                AND NEW.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
368
        THEN
369
            RAISE EXCEPTION 'TB4. Opération interdite (schéma %). Le champ creation ne peut passer de Vrai à Faux si le schéma existe.', NEW.nom_schema
370
                USING HINT =  'Si vous supprimez physiquement le schéma avec la commande DROP SCHEMA, creation basculera sur False automatiquement.' ;
371
        END IF ;
372
    END IF ;
373
    
374
    IF TG_OP <> 'DELETE'
375
    THEN
376
        ------ PROHIBITION DE LA SAISIE MANUELLE DES OID ------
377
        -- vérifié grâce au champ ctrl
378
        IF NEW.ctrl[2] IS NULL
379
            OR NOT array_length(NEW.ctrl, 1) >= 2
380
            OR NEW.ctrl[1] IS NULL
381
            OR NOT NEW.ctrl[1] IN ('CREATE', 'RENAME', 'OWNER', 'DROP', 'SELF', 'EXIT')
382
            OR NOT NEW.ctrl[2] = 'x7-A;#rzo'
383
            -- ctrl NULL ou invalide
384
        THEN
385

    
386
            IF NEW.ctrl[1] = 'EXIT'
387
            THEN
388
                RAISE EXCEPTION 'TB17. Opération interdite (schéma %).', coalesce(NEW.nom_schema, '?')
389
                    USING HINT = 'Pour déréférencer un schéma, veuillez utiliser la fonction z_asgard_admin.asgard_sortie_gestion_schema.' ;
390
            END IF ;
391
            
392
            -- réinitialisation du champ ctrl, qui peut contenir des informations
393
            -- issues de commandes antérieures (dans ctrl[1])
394
            NEW.ctrl := ARRAY['MANUEL', NULL]::text[] ;
395
            
396
            IF TG_OP = 'INSERT' AND (
397
                    NEW.oid_producteur IS NOT NULL
398
                    OR NEW.oid_lecteur IS NOT NULL
399
                    OR NEW.oid_editeur IS NOT NULL
400
                    OR NEW.oid_schema IS NOT NULL
401
                    )
402
            -- cas d'un INSERT manuel pour lequel des OID ont été saisis
403
            -- on les remet à NULL
404
            THEN
405
                NEW.oid_producteur = NULL ;
406
                NEW.oid_editeur = NULL ;
407
                NEW.oid_lecteur = NULL ;
408
                NEW.oid_schema = NULL ;
409
            ELSIF TG_OP = 'UPDATE'
410
            THEN
411
                IF NOT coalesce(NEW.oid_producteur, -1) = coalesce(OLD.oid_producteur, -1)
412
                        OR NOT coalesce(NEW.oid_editeur, -1) = coalesce(OLD.oid_editeur, -1)
413
                        OR NOT coalesce(NEW.oid_lecteur, -1) = coalesce(OLD.oid_lecteur, -1)
414
                        OR NOT coalesce(NEW.oid_schema, -1) = coalesce(OLD.oid_schema, -1)
415
                -- cas d'un UPDATE avec modification des OID
416
                -- on les remet à OLD
417
                THEN
418
                    NEW.oid_producteur = OLD.oid_producteur ;
419
                    NEW.oid_editeur = OLD.oid_editeur ;
420
                    NEW.oid_lecteur = OLD.oid_lecteur ;
421
                    NEW.oid_schema = OLD.oid_schema ;
422
                END IF ;
423
            END IF ;                
424
        ELSE
425
            -- suppression du mot de passe de contrôle.
426
            -- ctrl[1] est par contre conservé - il sera utilisé
427
            -- par le trigger AFTER pour connaître l'opération
428
            -- à l'origine de son déclenchement.
429
            NEW.ctrl[2] := NULL ;
430
        END IF ;
431
        
432
        ------ REQUETES AUTO A IGNORER ------
433
        -- les remontées du trigger AFTER (SELF)
434
        -- sont exclues, car les contraintes ont déjà
435
        -- été validées (et pose problèmes avec les
436
        -- contrôles d'OID sur les UPDATE, car ceux-ci
437
        -- ne seront pas nécessairement déjà remplis) ;
438
        -- les requêtes EXIT de même, car c'est un
439
        -- pré-requis à la suppression qui ne fait
440
        -- que modifier le champ ctrl
441
        IF NEW.ctrl[1] IN ('SELF', 'EXIT')
442
        THEN
443
            -- aucune action
444
            RETURN NEW ;
445
        END IF ;
446
        
447
        ------ VERROUILLAGE DES CHAMPS LIES A LA NOMENCLATURE ------
448
        -- modifiables uniquement par l'ADL
449
        IF TG_OP = 'UPDATE'
450
        THEN
451
            IF (OLD.nomenclature OR NEW.nomenclature) AND NOT pg_has_role('g_admin', 'MEMBER') AND (
452
                    NOT coalesce(OLD.nomenclature, False) = coalesce(NEW.nomenclature, False)
453
                    OR NOT coalesce(OLD.niv1, '') = coalesce(NEW.niv1, '')
454
                    OR NOT coalesce(OLD.niv1_abr, '') = coalesce(NEW.niv1_abr, '')
455
                    OR NOT coalesce(OLD.niv2, '') = coalesce(NEW.niv2, '')
456
                    OR NOT coalesce(OLD.niv2_abr, '') = coalesce(NEW.niv2_abr, '')
457
                    OR NOT coalesce(OLD.nom_schema, '') = coalesce(NEW.nom_schema, '')
458
                    OR NOT coalesce(OLD.bloc, '') = coalesce(NEW.bloc, '')
459
                    )
460
            THEN
461
                RAISE EXCEPTION 'TB18. Opération interdite (schéma %).', NEW.nom_schema
462
                    USING HINT = 'Seuls les membres de g_admin sont habilités à modifier les champs nomenclature et - pour les schémas de la nomenclature - bloc, niv1, niv1_abr, niv2, niv2_abr et nom_schema.' ;
463
            END IF ;
464
        ELSIF TG_OP = 'INSERT'
465
        THEN
466
            IF NEW.nomenclature AND NOT pg_has_role('g_admin', 'MEMBER')
467
            THEN
468
                RAISE EXCEPTION 'TB19. Opération interdite (schéma %).', NEW.nom_schema
469
                    USING HINT = 'Seuls les membres de g_admin sont autorisés à ajouter des schémas à la nomenclature (nomenclature = True).' ;
470
            END IF ;
471
        END IF ;
472
    
473
        ------ NETTOYAGE DES CHAÎNES VIDES ------
474
        -- si l'utilisateur a entré des chaînes vides on met des NULL
475
        NEW.editeur := nullif(NEW.editeur, '') ;
476
        NEW.lecteur := nullif(NEW.lecteur, '') ;
477
        NEW.bloc := nullif(NEW.bloc, '') ;
478
        NEW.niv1 := nullif(NEW.niv1, '') ;
479
        NEW.niv1_abr := nullif(NEW.niv1_abr, '') ;
480
        NEW.niv2 := nullif(NEW.niv2, '') ;
481
        NEW.niv2_abr := nullif(NEW.niv2_abr, '') ;
482
        NEW.nom_schema := nullif(NEW.nom_schema, '') ;
483
        -- si producteur est vide on met par défaut g_admin
484
        NEW.producteur := coalesce(nullif(NEW.producteur, ''), 'g_admin') ;
485
        
486
        ------ NETTOYAGE DES CHAMPS OID ------
487
        -- pour les rôles de lecteur et éditeur,
488
        -- si le champ de nom est vidé par l'utilisateur,
489
        -- on vide en conséquence l'OID
490
        IF NEW.editeur IS NULL
491
        THEN
492
            NEW.oid_editeur := NULL ;
493
        END IF ;
494
        IF NEW.lecteur IS NULL
495
        THEN
496
            NEW.oid_lecteur := NULL ;
497
        END IF ;
498
        -- si le schéma n'est pas créé, on s'assure que les champs
499
        -- d'OID restent vides
500
        -- à noter que l'event trigger sur DROP SCHEMA vide
501
        -- déjà le champ oid_schema
502
        IF NOT NEW.creation
503
        THEN
504
            NEW.oid_schema := NULL ;
505
            NEW.oid_lecteur := NULL ;
506
            NEW.oid_editeur := NULL ;
507
            NEW.oid_producteur := NULL ;
508
        END IF ;
509
        
510
        ------ VALIDITE DES NOMS DE ROLES ------
511
        -- dans le cas d'un schéma pré-existant, on s'assure que les rôles qui
512
        -- ne changent pas sont toujours valides (qu'ils existent et que le nom
513
        -- n'a pas été modifié entre temps)
514
        -- si tel est le cas, on les met à jour et on le note dans
515
        -- ctrl, pour que le trigger AFTER sache qu'il ne s'agit
516
        -- pas réellement de nouveaux rôles sur lesquels les droits
517
        -- devraient être réappliqués
518
        IF TG_OP = 'UPDATE' AND NEW.creation
519
        THEN
520
            -- producteur
521
            IF OLD.creation AND OLD.producteur = NEW.producteur
522
            THEN
523
                SELECT rolname INTO n_role
524
                    FROM pg_catalog.pg_roles
525
                    WHERE pg_roles.oid = NEW.oid_producteur ;
526
                IF NOT FOUND
527
                -- le rôle producteur n'existe pas
528
                THEN
529
                    -- cas invraisemblable, car un rôle ne peut pas être
530
                    -- supprimé alors qu'il est propriétaire d'un schéma, et la
531
                    -- commande ALTER SCHEMA OWNER TO aurait été interceptée
532
                    -- mais, s'il advient, on repart du propriétaire
533
                    -- renseigné dans pg_namespace
534
                    SELECT replace(nspowner::regrole::text, '"', ''), nspowner
535
                        INTO NEW.producteur, NEW.oid_producteur
536
                        FROM pg_catalog.pg_namespace
537
                        WHERE pg_namespace.oid = NEW.oid_schema ;
538
                    RAISE NOTICE '[table de gestion] ANOMALIE. Schéma %. L''OID actuellement renseigné pour le producteur est invalide. Poursuite avec l''OID du propriétaire courant du schéma.', NEW.nom_schema ;
539
                    NEW.ctrl := array_append(NEW.ctrl, 'CLEAN producteur') ;
540
                ELSIF NOT n_role = NEW.producteur
541
                -- libellé obsolète du producteur
542
                THEN
543
                    NEW.producteur := n_role ;
544
                    RAISE NOTICE '[table de gestion] Schéma %. Mise à jour du libellé du rôle producteur, renommé entre temps.', NEW.nom_schema
545
                        USING DETAIL = 'Ancien nom "' || OLD.producteur || '", nouveau nom "' || NEW.producteur || '".' ;
546
                    NEW.ctrl := array_append(NEW.ctrl, 'CLEAN producteur') ;
547
                END IF ; 
548
            END IF ;
549
            -- éditeur
550
            IF OLD.creation AND OLD.editeur = NEW.editeur
551
                    AND NOT NEW.editeur = 'public'
552
            THEN
553
                SELECT rolname INTO n_role
554
                    FROM pg_catalog.pg_roles
555
                    WHERE pg_roles.oid = NEW.oid_editeur ;
556
                IF NOT FOUND
557
                -- le rôle éditeur n'existe pas
558
                THEN
559
                    NEW.editeur := NULL ;
560
                    NEW.oid_editeur := NULL ;
561
                    RAISE NOTICE '[table de gestion] Schéma %. Le rôle éditeur n''existant plus, il est déréférencé.', NEW.nom_schema
562
                        USING DETAIL = 'Ancien nom "' || OLD.editeur || '".' ;
563
                    NEW.ctrl := array_append(NEW.ctrl, 'CLEAN editeur') ;
564
                ELSIF NOT n_role = NEW.editeur
565
                -- libellé obsolète de l'éditeur
566
                THEN
567
                    NEW.editeur := n_role ;
568
                    RAISE NOTICE '[table de gestion] Schéma %. Mise à jour du libellé du rôle éditeur, renommé entre temps.', NEW.nom_schema
569
                        USING DETAIL = 'Ancien nom "' || OLD.editeur || '", nouveau nom "' || NEW.editeur || '".' ;
570
                    NEW.ctrl := array_append(NEW.ctrl, 'CLEAN editeur') ;
571
                END IF ; 
572
            END IF ;
573
            -- lecteur
574
            IF OLD.creation AND OLD.lecteur = NEW.lecteur
575
                    AND NOT NEW.lecteur = 'public'
576
            THEN
577
                SELECT rolname INTO n_role
578
                    FROM pg_catalog.pg_roles
579
                    WHERE pg_roles.oid = NEW.oid_lecteur ;
580
                IF NOT FOUND
581
                -- le rôle lecteur n'existe pas
582
                THEN
583
                    NEW.lecteur := NULL ;
584
                    NEW.oid_lecteur := NULL ;
585
                    RAISE NOTICE '[table de gestion] Schéma %. Le rôle lecteur n''existant plus, il est déréférencé.', NEW.nom_schema
586
                        USING DETAIL = 'Ancien nom "' || OLD.lecteur || '".' ;
587
                    NEW.ctrl := array_append(NEW.ctrl, 'CLEAN lecteur') ;
588
                ELSIF NOT n_role = NEW.lecteur
589
                -- libellé obsolète du lecteur
590
                THEN
591
                    NEW.lecteur := n_role ;
592
                    RAISE NOTICE '[table de gestion] Schéma %. Mise à jour du libellé du rôle lecteur, renommé entre temps.', NEW.nom_schema
593
                        USING DETAIL = 'Ancien nom "' || OLD.lecteur || '", nouveau nom "' || NEW.lecteur || '".' ;
594
                    NEW.ctrl := array_append(NEW.ctrl, 'CLEAN lecteur') ;
595
                END IF ; 
596
            END IF ;    
597
        END IF ;
598

    
599
        ------ NON RESPECT DES CONTRAINTES ------
600
        -- non nullité de nom_schema
601
        IF NEW.nom_schema IS NULL
602
        THEN
603
            RAISE EXCEPTION 'TB8. Saisie incorrecte. Le nom du schéma doit être renseigné (champ nom_schema).' ;
604
        END IF ;
605
        
606
        -- unicité de nom_schema
607
        -- -> contrôlé après les manipulations sur les blocs de
608
        -- la partie suivante.
609
        
610
        -- unicité de oid_schema
611
        IF TG_OP = 'INSERT' AND NEW.oid_schema IN (SELECT gestion_schema_etr.oid_schema FROM z_asgard.gestion_schema_etr
612
                                                       WHERE gestion_schema_etr.oid_schema IS NOT NULL)
613
        THEN
614
            RAISE EXCEPTION 'TB11. Saisie incorrecte (schéma %). Un schéma de même OID est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
615
        ELSIF TG_OP = 'UPDATE'
616
        THEN
617
            -- cas (très hypothétique) d'une modification d'OID
618
            IF NOT coalesce(NEW.oid_schema, -1) = coalesce(OLD.oid_schema, -1)
619
                    AND NEW.oid_schema IN (SELECT gestion_schema_etr.oid_schema FROM z_asgard.gestion_schema_etr
620
                                                       WHERE gestion_schema_etr.oid_schema IS NOT NULL)
621
            THEN
622
                RAISE EXCEPTION 'TB12. Saisie incorrecte (schéma %). Un schéma de même OID est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
623
            END IF ;
624
        END IF ;
625
        
626
        -- non répétition des rôles
627
        IF NOT ((NEW.oid_lecteur IS NULL OR NOT NEW.oid_lecteur = NEW.oid_producteur)
628
                AND (NEW.oid_editeur IS NULL OR NOT NEW.oid_editeur = NEW.oid_producteur)
629
                AND (NEW.oid_lecteur IS NULL OR NEW.oid_editeur IS NULL OR NOT NEW.oid_lecteur = NEW.oid_editeur))
630
        THEN
631
            RAISE EXCEPTION 'TB13. Saisie incorrecte (schéma %). Les rôles producteur, lecteur et éditeur doivent être distincts.', NEW.nom_schema ;
632
        END IF ;
633
    END IF ;
634
    
635
    ------ COHERENCE BLOC/NOM DU SCHEMA ------
636
    IF TG_OP IN ('INSERT', 'UPDATE')
637
    THEN
638
        IF NEW.bloc IS NULL AND NEW.nom_schema ~ '^[a-z]_'
639
        -- si bloc est NULL, mais que le nom du schéma
640
        -- comporte un préfixe, on met ce préfixe dans bloc
641
        THEN
642
            NEW.bloc := substring(NEW.nom_schema, '^([a-z])_') ;
643
            RAISE NOTICE '[table de gestion] Mise à jour du bloc pour le schéma %.', NEW.nom_schema || ' (' || NEW.bloc || ')' ;
644
        ELSIF NEW.bloc IS NULL
645
        -- si bloc est NULL, et que (sous-entendu) le nom du schéma ne
646
        -- respecte pas la nomenclature, on avertit l'utilisateur
647
        THEN            
648
            RAISE NOTICE '[table de gestion] Le nom du schéma % ne respecte pas la nomenclature.', NEW.nom_schema
649
                USING HINT = 'Si vous saisissez un préfixe dans le champ bloc, il sera automatiquement ajouté au nom du schéma.' ;
650
        ELSIF NOT NEW.nom_schema ~ ('^'|| NEW.bloc || '_') AND NOT NEW.bloc = 'd'
651
        -- le bloc est renseigné mais le nom du schéma ne correspond pas
652
        -- (et il ne s'agit pas d'un schéma mis à la corbeille) :
653
        -- si le nom est de la forme 'a_...', alors :
654
        -- - dans le cas d'un UPDATE avec modification du nom
655
        -- du schéma et pas du bloc, on se fie au nom du schéma
656
        -- et on change le bloc ;
657
        -- - si bloc n'est pas une lettre, on renvoie une erreur ;
658
        -- - dans les autres cas, on se fie au bloc et change le
659
        -- préfixe.
660
        -- Si le nom ne comporte pas de préfixe :
661
        -- - si le bloc est une lettre, on l'ajoute au début du
662
        -- nom (sans doubler l'underscore, si le nom commençait par
663
        -- un underscore) ;
664
        -- - sinon on renvoie une erreur.
665
        THEN
666
            IF NEW.nom_schema ~ '^([a-z])?_'
667
            THEN
668
                IF TG_OP = 'UPDATE'
669
                THEN
670
                    IF NOT NEW.nom_schema = OLD.nom_schema AND NEW.bloc = OLD.bloc
671
                    THEN
672
                        NEW.bloc := substring(NEW.nom_schema, '^([a-z])_') ;
673
                        RAISE NOTICE '[table de gestion] Mise à jour du bloc pour le schéma %.', NEW.nom_schema || ' (' || NEW.bloc || ')' ;
674
                    ELSIF NOT NEW.bloc ~ '^[a-z]$'
675
                    THEN
676
                        RAISE EXCEPTION 'TB14. Saisie invalide (schéma %). Le bloc doit être une lettre minuscule ou rien.', NEW.nom_schema ;
677
                    ELSE
678
                        NEW.nom_schema := regexp_replace(NEW.nom_schema, '^([a-z])?_', NEW.bloc || '_') ;
679
                        RAISE NOTICE '[table de gestion] Mise à jour du préfixe du schéma %.', NEW.nom_schema || ' d''après son bloc (' || NEW.bloc || ')' ;
680
                    END IF ;
681
                ELSIF NOT NEW.bloc ~ '^[a-z]$'
682
                THEN
683
                    RAISE EXCEPTION 'TB15. Saisie invalide (schéma %). Le bloc doit être une lettre minuscule ou rien.', NEW.nom_schema ;
684
                ELSE
685
                    NEW.nom_schema := regexp_replace(NEW.nom_schema, '^([a-z])?_', NEW.bloc || '_') ;
686
                    RAISE NOTICE '[table de gestion] Mise à jour du préfixe du schéma %.', NEW.nom_schema || ' d''après son bloc (' || NEW.bloc || ')' ;
687
                END IF ;
688
            ELSIF NOT NEW.bloc ~ '^[a-z]$'
689
            THEN
690
                RAISE EXCEPTION 'TB16. Saisie invalide (schéma %). Le bloc doit être une lettre minuscule ou rien.', NEW.nom_schema ;
691
            ELSE
692
                NEW.nom_schema := NEW.bloc || '_' || NEW.nom_schema ;
693
                RAISE NOTICE '[table de gestion] Mise à jour du préfixe du schéma %.', NEW.nom_schema || ' d''après son bloc (' || NEW.bloc || ')' ;
694
            END IF ;
695
            -- le trigger AFTER se chargera de renommer physiquement le
696
            -- schéma d'autant que de besoin
697
        END IF ;
698
    END IF ;
699
    
700
    ------ NON RESPECT DES CONTRAINTES (SUITE) ------
701
    -- unicité de nom_schema
702
    IF TG_OP IN ('INSERT', 'UPDATE')
703
    THEN
704
        IF TG_OP = 'INSERT' AND NEW.nom_schema IN (SELECT gestion_schema_etr.nom_schema FROM z_asgard.gestion_schema_etr)
705
        THEN
706
            RAISE EXCEPTION 'TB9. Saisie incorrecte (schéma %). Un schéma de même nom est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
707
        ELSIF TG_OP = 'UPDATE'
708
        THEN
709
            -- cas d'un changement de nom
710
            IF NOT NEW.nom_schema = OLD.nom_schema
711
                   AND NEW.nom_schema IN (SELECT gestion_schema_etr.nom_schema FROM z_asgard.gestion_schema_etr)
712
            THEN 
713
                RAISE EXCEPTION 'TB10. Saisie incorrecte (schéma %). Un schéma de même nom est déjà répertorié dans la table de gestion.', NEW.nom_schema ;
714
            END IF ;
715
        END IF ;
716
    END IF ;
717
    
718
    ------ MISE À LA CORBEILLE ------
719
    -- notification de l'utilisateur
720
    IF TG_OP = 'UPDATE'
721
    THEN
722
        -- schéma existant dont bloc bascule sur 'd'
723
        -- ou schéma créé par bascule de creation sur True dans bloc vaut 'd'
724
        IF NEW.creation AND NEW.bloc = 'd' AND (NOT OLD.bloc = 'd' OR OLD.bloc IS NULL)
725
                OR NEW.creation AND NOT OLD.creation AND NEW.bloc = 'd'
726
        THEN
727
            RAISE NOTICE '[table de gestion] Le schéma % a été mis à la corbeille (bloc = ''d'').', NEW.nom_schema
728
                USING HINT = 'Si vous basculez creation sur False, le schéma et son contenu seront automatiquement supprimés.' ;
729
        -- restauration
730
        ELSIF NEW.creation AND OLD.bloc = 'd' AND (NOT NEW.bloc = 'd' OR NEW.bloc IS NULL)
731
        THEN
732
            RAISE NOTICE '[table de gestion] Le schéma % a été retiré de la corbeille (bloc ne vaut plus ''d'').', NEW.nom_schema ;
733
        END IF ;
734
    ELSIF TG_OP = 'INSERT'
735
    THEN
736
        -- nouveau schéma dont bloc vaut 'd'
737
        IF NEW.creation AND NEW.bloc = 'd'
738
        THEN
739
            RAISE NOTICE '[table de gestion] Le schéma % a été mis à la corbeille (bloc = ''d'').', NEW.nom_schema
740
                USING HINT = 'Si vous basculez creation sur False, le schéma et son contenu seront automatiquement supprimés.' ;  
741
        END IF ;
742
    END IF ;
743
    
744
    ------ RETURN ------
745
	IF TG_OP IN ('UPDATE', 'INSERT')
746
    THEN
747
        RETURN NEW ;
748
    ELSIF TG_OP = 'DELETE'
749
    THEN
750
        RETURN OLD ;
751
    END IF ;
752
    
753
END
754
$BODY$ ;
755

    
756

    
757
------ 5.2 - TRIGGER AFTER ------
758

    
759
-- FUNCTION: z_asgard_admin.asgard_on_modify_gestion_schema_after()
760

    
761
CREATE OR REPLACE FUNCTION z_asgard_admin.asgard_on_modify_gestion_schema_after() RETURNS trigger
762
    LANGUAGE plpgsql
763
    AS $BODY$
764
/* OBJET : Fonction exécutée par le trigger asgard_on_modify_gestion_schema_after,
765
           qui répercute physiquement les modifications de la table de gestion.
766
CIBLES : z_asgard_admin.gestion_schema.
767
PORTEE : FOR EACH ROW.
768
DECLENCHEMENT : AFTER INSERT OR UPDATE.*/
769
DECLARE
770
    utilisateur text ;
771
    createur text ;
772
    administrateur text ;
773
    e_mssg text ;
774
    e_hint text ;
775
    e_detl text ;
776
    b_superuser boolean ;
777
    b_test boolean ;
778
    l_commande text[] ;
779
    c text ;
780
    c_reverse text ;
781
    a_producteur text ;
782
    a_editeur text ;
783
    a_lecteur text ;
784
    n int ;
785
BEGIN
786

    
787
    ------ REQUETES AUTO A IGNORER ------
788
    -- les remontées du trigger lui-même (SELF),
789
    -- ainsi que des event triggers sur les
790
    -- suppressions de schémas (DROP), n'appellent
791
    -- aucune action, elles sont donc exclues dès
792
    -- le départ
793
    -- les remontées des changements de noms sont
794
    -- conservées, pour le cas où la mise en
795
    -- cohérence avec "bloc" aurait conduit à une
796
    -- modification du nom par le trigger BEFORE
797
    -- (géré au point suivant)
798
    -- les remontées des créations et changements
799
    -- de propriétaire (CREATE et OWNER) appellent
800
    -- des opérations sur les droits plus lourdes
801
    -- qui ne permettent pas de les exclure en
802
    -- amont
803
    IF NEW.ctrl[1] IN ('SELF', 'DROP')
804
    THEN
805
        -- aucune action
806
        RETURN NULL ;
807
    END IF ;
808

    
809
    ------ MANIPULATIONS PREALABLES ------
810
    utilisateur := current_user ;
811
    
812
    -- si besoin pour les futures opérations sur les rôles,
813
    -- récupération du nom d'un rôle dont current_user est membre
814
    -- et qui a l'attribut CREATEROLE. Autant que possible, la
815
    -- requête renvoie current_user lui-même. On exclut d'office les
816
    -- rôles NOINHERIT qui ne pourront pas avoir simultanément les
817
    -- droits du propriétaire de NEW et OLD.producteur
818
    SELECT rolname INTO createur FROM pg_roles
819
        WHERE pg_has_role(rolname, 'MEMBER') AND rolcreaterole AND rolinherit
820
        ORDER BY rolname = current_user DESC ;
821
    
822
    IF TG_OP = 'UPDATE'
823
    THEN
824
        -- la validité de OLD.producteur n'ayant
825
        -- pas été contrôlée par le trigger BEFORE,
826
        -- on le fait maintenant
827
        SELECT rolname INTO a_producteur
828
            FROM pg_catalog.pg_roles
829
            WHERE pg_roles.oid = OLD.oid_producteur ;
830
        -- pour la suite, on emploira toujours
831
        -- a_producteur à la place de OLD.producteur
832
        -- pour les opérations sur les droits.
833
        -- Il est réputé non NULL pour un schéma
834
        -- pré-existant (OLD.creation vaut True),
835
        -- dans la mesure où un rôle ne peut être
836
        -- supprimé s'il est propriétaire d'un
837
        -- schéma et où tous les changements de
838
        -- propriétaires sont remontés par event
839
        -- triggers (+ contrôles pour assurer la
840
        -- non-modification manuelle des OID).
841
        IF NOT FOUND AND OLD.creation AND NOT 'CLEAN producteur' = ANY(NEW.ctrl)
842
        THEN
843
            RAISE NOTICE '[table de gestion] ANOMALIE. Schéma %. L''OID actuellement renseigné pour le producteur dans la table de gestion est invalide. Poursuite avec l''OID du propriétaire courant du schéma.', OLD.nom_schema ;
844
            SELECT replace(nspowner::regrole::text, '"', '') INTO a_producteur
845
                FROM pg_catalog.pg_namespace
846
                WHERE pg_namespace.oid = NEW.oid_schema ;
847
            IF NOT FOUND
848
            THEN
849
                RAISE EXCEPTION 'TA1. Anomalie critique (schéma %). Le propriétaire du schéma est introuvable.', OLD.nom_schema ;
850
            END IF ;
851
        END IF ;
852
    END IF ;
853

    
854
    ------ MISE EN APPLICATION D'UN CHANGEMENT DE NOM DE SCHEMA ------
855
    IF NOT NEW.oid_schema::regnamespace::text = quote_ident(NEW.nom_schema)
856
    -- le schéma existe et ne porte pas déjà le nom NEW.nom_schema
857
    THEN
858
        EXECUTE 'ALTER SCHEMA '|| NEW.oid_schema::regnamespace::text ||
859
                ' RENAME TO ' || quote_ident(NEW.nom_schema) ;
860
        RAISE NOTICE '... Le schéma % a été renommé.', NEW.nom_schema ;
861
    END IF ; 
862
    -- exclusion des remontées d'event trigger correspondant
863
    -- à des changements de noms
864
    IF NEW.ctrl[1] = 'RENAME'
865
    THEN
866
        -- aucune action
867
        RETURN NULL ;
868
    END IF ;
869

    
870
    ------ PREPARATION DU PRODUCTEUR ------
871
    -- on ne s'intéresse pas aux cas :
872
    -- - d'un schéma qui n'a pas/plus vocation à exister
873
    --   (creation vaut False) ;
874
    -- - d'un schéma pré-existant dont les rôles ne changent pas
875
    --   ou dont le libellé a juste été nettoyé par le trigger
876
    --   BEFORE.
877
    -- ils sont donc exclus au préalable
878
    -- si le moindre rôle a changé, il faudra être membre du
879
    -- groupe propriétaire/producteur pour pouvoir modifier
880
    -- les privilèges en conséquence
881
    b_test := False ;
882
    IF NOT NEW.creation
883
    THEN
884
        b_test := True ;
885
    ELSIF TG_OP = 'UPDATE'
886
    THEN
887
        IF OLD.creation
888
                AND (NEW.producteur = OLD.producteur  OR 'CLEAN producteur' = ANY(NEW.ctrl))
889
                AND (coalesce(NEW.editeur, '') = coalesce(OLD.editeur, '') OR 'CLEAN editeur' = ANY(NEW.ctrl))
890
                AND (coalesce(NEW.lecteur, '') = coalesce(OLD.lecteur, '') OR 'CLEAN lecteur' = ANY(NEW.ctrl))
891
        THEN
892
            b_test := True ;
893
        END IF ;
894
    END IF ;
895
    
896
    IF NOT b_test
897
    THEN
898
        IF NOT NEW.producteur IN (SELECT rolname FROM pg_catalog.pg_roles)
899
        -- si le producteur désigné n'existe pas, on le crée
900
        -- ou renvoie une erreur si les privilèges de l'utilisateur
901
        -- sont insuffisants
902
        THEN
903
            IF createur IS NULL
904
            THEN
905
                RAISE EXCEPTION 'TA2. Opération interdite. Vous n''êtes pas habilité à créer le rôle %.', NEW.producteur
906
                    USING HINT = 'Être membre d''un rôle disposant des attributs CREATEROLE et INHERIT est nécessaire pour créer de nouveaux producteurs.' ;
907
            END IF ;
908
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;
909
            EXECUTE 'CREATE ROLE ' || quote_ident(NEW.producteur) ;
910
            RAISE NOTICE '... Le rôle de groupe % a été créé.', NEW.producteur ;
911
            EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;                
912
        ELSE
913
        -- si le rôle producteur existe, on vérifie qu'il n'a pas l'option LOGIN
914
        -- les superusers avec LOGIN (comme postgres) sont tolérés
915
        -- paradoxe ou non, dans l'état actuel des choses, cette erreur se
916
        -- déclenche aussi lorsque la modification ne porte que sur les rôles
917
        -- lecteur/éditeur
918
            SELECT rolsuper INTO b_superuser
919
                FROM pg_roles WHERE rolname = NEW.producteur AND rolcanlogin ;
920
            IF NOT b_superuser
921
            THEN
922
                RAISE EXCEPTION 'TA3. Opération interdite (schéma %). Le producteur/propriétaire du schéma ne doit pas être un rôle de connexion.', NEW.nom_schema ;
923
            END IF ;
924
        END IF ;
925
        b_superuser := coalesce(b_superuser, False) ;
926
        
927
        -- mise à jour du champ d'OID du producteur
928
        IF NEW.ctrl[1] IS NULL OR NOT NEW.ctrl[1] IN ('OWNER', 'CREATE')
929
        -- pas dans le cas d'une remontée de commande directe
930
        -- où l'OID du producteur sera déjà renseigné
931
        -- et uniquement s'il a réellement été modifié (ce
932
        -- qui n'est pas le cas si les changements ne portent
933
        -- que sur les rôles lecteur/éditeur)
934
        THEN
935
            UPDATE z_asgard.gestion_schema_etr
936
                SET oid_producteur = quote_ident(NEW.producteur)::regrole::oid,
937
                    ctrl = ARRAY['SELF', 'x7-A;#rzo']
938
                WHERE nom_schema = NEW.nom_schema AND (
939
                    oid_producteur IS NULL
940
                    OR NOT oid_producteur = quote_ident(NEW.producteur)::regrole::oid
941
                    ) ;
942
        END IF ;
943

    
944
        -- implémentation des permissions manquantes sur NEW.producteur
945
        IF NOT pg_has_role(utilisateur, NEW.producteur, 'USAGE')
946
        THEN
947
            b_test := True ;
948
            IF createur IS NULL OR b_superuser
949
            THEN
950
                RAISE EXCEPTION 'TA4. Opération interdite. Permissions insuffisantes pour le rôle %.', NEW.producteur
951
                    USING HINT = 'Votre rôle doit être membre de ' || NEW.producteur
952
                                     || ' ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ;
953
            END IF ;
954
        END IF ;
955
        IF TG_OP = 'UPDATE'
956
        THEN
957
            IF OLD.creation AND NOT pg_has_role(utilisateur, a_producteur, 'USAGE')
958
                AND NOT (NEW.producteur = OLD.producteur  OR 'CLEAN producteur' = ANY(NEW.ctrl))
959
                -- les permissions sur OLD.producteur ne sont contrôlées que si le producteur
960
                -- a effectivement été modifié
961
            THEN
962
                b_test := True ;
963
                IF createur IS NULL OR b_superuser
964
                THEN
965
                    RAISE EXCEPTION 'TA5. Opération interdite. Permissions insuffisantes pour le rôle %.', a_producteur
966
                        USING HINT = 'Votre rôle doit être membre de ' || a_producteur
967
                                         || ' ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ;
968
                END IF ;            
969
            END IF ;
970
        END IF ;       
971
        IF b_test
972
        THEN
973
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;            
974
            -- par commodité, on rend createur membre à la fois de NEW et (si besoin)
975
            -- de OLD.producteur, même si l'utilisateur avait déjà accès à
976
            -- l'un des deux par ailleurs :
977
            IF NOT pg_has_role(createur, NEW.producteur, 'USAGE') AND NOT b_superuser
978
            THEN
979
                EXECUTE 'GRANT ' || quote_ident(NEW.producteur) || ' TO ' || quote_ident(createur) ;
980
                RAISE NOTICE '... Permission accordée à %.', createur || ' sur le rôle ' || NEW.producteur ;
981
            END IF ;
982
            IF TG_OP = 'UPDATE'
983
            THEN
984
                IF NOT pg_has_role(createur, a_producteur, 'USAGE') AND NOT b_superuser
985
                THEN
986
                    EXECUTE 'GRANT ' || quote_ident(a_producteur) || ' TO ' || quote_ident(createur) ;
987
                    RAISE NOTICE '... Permission accordée à %.', createur || ' sur le rôle ' || a_producteur ;
988
                END IF ;
989
            END IF ;
990
            EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
991
        END IF ;
992
           
993
        -- permission de g_admin sur le producteur, s'il y a encore lieu
994
        -- à noter que, dans le cas où le producteur n'a pas été modifié, g_admin
995
        -- devrait déjà avoir une permission sur NEW.producteur, sauf à ce qu'elle
996
        -- lui ait été retirée manuellement entre temps. Les requêtes suivantes
997
        -- génèreraient alors une erreur même dans le cas où la modification ne
998
        -- porte que sur les rôles lecteur/éditeur - ce qui peut-être perçu comme
999
        -- discutable.
1000
        IF NOT pg_has_role('g_admin', NEW.producteur, 'USAGE') AND NOT b_superuser
1001
        THEN
1002
            IF createur IS NOT NULL
1003
            THEN
1004
                EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1005
                EXECUTE 'GRANT ' || quote_ident(NEW.producteur) || ' TO g_admin' ;
1006
                RAISE NOTICE '... Permission accordée à g_admin sur le rôle %.', NEW.producteur ;
1007
                EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1008
            ELSE
1009
                SELECT grantee INTO administrateur
1010
                    FROM information_schema.applicable_roles
1011
                    WHERE is_grantable = 'YES' AND role_name = NEW.producteur ;
1012
                IF FOUND
1013
                THEN
1014
                    EXECUTE 'SET ROLE ' || quote_ident(administrateur) ;
1015
                    EXECUTE 'GRANT ' || quote_ident(NEW.producteur) || ' TO g_admin' ;
1016
                    RAISE NOTICE '... Permission accordée à g_admin sur le rôle %.', NEW.producteur ;
1017
                    EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1018
                ELSE
1019
                    RAISE EXCEPTION 'TA6. Opération interdite. Permissions insuffisantes pour le rôle %.', NEW.producteur
1020
                        USING DETAIL = 'GRANT ' || quote_ident(NEW.producteur) || ' TO g_admin',
1021
                              HINT = 'Votre rôle doit être membre de ' || NEW.producteur
1022
                                         || ' avec admin option ou disposer de l''attribut CREATEROLE pour réaliser cette opération.' ;
1023
                END IF ;
1024
            END IF ;
1025
        END IF ;
1026
    END IF ;
1027
    
1028
    ------ PREPARATION DE L'EDITEUR ------
1029
    -- limitée ici à la création du rôle et l'implémentation
1030
    -- de son OID. On ne s'intéresse donc pas aux cas :
1031
    -- - où il y a pas d'éditeur ;
1032
    -- - d'un schéma qui n'a pas/plus vocation à exister ;
1033
    -- - d'un schéma pré-existant dont l'éditeur ne change pas
1034
    --   ou dont le libellé a seulement été nettoyé par le
1035
    --   trigger BEFORE.
1036
    -- ils sont donc exclus au préalable
1037
    b_test := False ;
1038
    IF NOT NEW.creation OR NEW.editeur IS NULL
1039
            OR 'CLEAN editeur' = ANY(NEW.ctrl)
1040
    THEN
1041
        b_test := True ;
1042
    ELSIF TG_OP = 'UPDATE'
1043
    THEN
1044
        IF OLD.creation AND NEW.editeur = OLD.editeur
1045
        THEN
1046
            b_test := True ;           
1047
        END IF ;
1048
    END IF ;
1049
    
1050
    IF NOT b_test
1051
    THEN
1052
        IF NOT NEW.editeur IN (SELECT rolname FROM pg_catalog.pg_roles)
1053
                AND NOT NEW.editeur = 'public'
1054
        -- si l'éditeur désigné n'existe pas, on le crée
1055
        -- ou renvoie une erreur si les privilèges de l'utilisateur
1056
        -- sont insuffisants
1057
        THEN
1058
            IF createur IS NULL
1059
            THEN
1060
                RAISE EXCEPTION 'TA7. Opération interdite. Vous n''êtes pas habilité à créer le rôle %.', NEW.editeur
1061
                    USING HINT = 'Être membre d''un rôle disposant des attributs CREATEROLE et INHERIT est nécessaire pour créer de nouveaux éditeurs.' ;
1062
            END IF ;
1063
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1064
            EXECUTE 'CREATE ROLE ' || quote_ident(NEW.editeur) ;
1065
            RAISE NOTICE '... Le rôle de groupe % a été créé.', NEW.editeur ;
1066
            EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1067
        END IF ;
1068
        
1069
        -- mise à jour du champ d'OID de l'éditeur
1070
        IF NEW.editeur = 'public'
1071
        THEN
1072
            UPDATE z_asgard.gestion_schema_etr
1073
                SET oid_editeur = 0,
1074
                    ctrl = ARRAY['SELF', 'x7-A;#rzo']
1075
                WHERE nom_schema = NEW.nom_schema AND (
1076
                    oid_editeur IS NULL
1077
                    OR NOT oid_editeur = 0
1078
                    ) ;
1079
        ELSE
1080
            UPDATE z_asgard.gestion_schema_etr
1081
                SET oid_editeur = quote_ident(NEW.editeur)::regrole::oid,
1082
                    ctrl = ARRAY['SELF', 'x7-A;#rzo']
1083
                WHERE nom_schema = NEW.nom_schema AND (
1084
                    oid_editeur IS NULL
1085
                    OR NOT oid_editeur = quote_ident(NEW.editeur)::regrole::oid
1086
                    ) ;
1087
        END IF ;
1088
    END IF ;
1089
    
1090
    ------ PREPARATION DU LECTEUR ------
1091
    -- limitée ici à la création du rôle et l'implémentation
1092
    -- de son OID. On ne s'intéresse donc pas aux cas :
1093
    -- - où il y a pas de lecteur ;
1094
    -- - d'un schéma qui n'a pas/plus vocation à exister ;
1095
    -- - d'un schéma pré-existant dont l'éditeur ne change pas
1096
    --   ou dont le libellé a seulement été nettoyé par le
1097
    --   trigger BEFORE.
1098
    -- ils sont donc exclus au préalable
1099
    b_test := False ;
1100
    IF NOT NEW.creation OR NEW.lecteur IS NULL
1101
            OR 'CLEAN lecteur' = ANY(NEW.ctrl)
1102
    THEN
1103
        b_test := True ;
1104
    ELSIF TG_OP = 'UPDATE'
1105
    THEN
1106
        IF OLD.creation AND NEW.lecteur = OLD.lecteur
1107
        THEN
1108
            b_test := True ;
1109
        END IF ;
1110
    END IF ;
1111
    
1112
    IF NOT b_test
1113
    THEN
1114
        IF NOT NEW.lecteur IN (SELECT rolname FROM pg_catalog.pg_roles)
1115
                AND NOT NEW.lecteur = 'public'
1116
        -- si le lecteur désigné n'existe pas, on le crée
1117
        -- ou renvoie une erreur si les privilèges de l'utilisateur
1118
        -- sont insuffisants
1119
        THEN
1120
            IF createur IS NULL
1121
            THEN
1122
                RAISE EXCEPTION 'TA8. Opération interdite. Vous n''êtes pas habilité à créer le rôle %.', NEW.lecteur
1123
                    USING HINT = 'Être membre d''un rôle disposant des attributs CREATEROLE et INHERIT est nécessaire pour créer de nouveaux éditeurs.' ;
1124
            END IF ;
1125
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1126
            EXECUTE 'CREATE ROLE ' || quote_ident(NEW.lecteur) ;
1127
            RAISE NOTICE '... Le rôle de groupe % a été créé.', NEW.lecteur ;
1128
            EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1129
        END IF ;
1130
        
1131
        -- mise à jour du champ d'OID du lecteur
1132
        IF NEW.lecteur = 'public'
1133
        THEN
1134
            UPDATE z_asgard.gestion_schema_etr
1135
                SET oid_lecteur = 0,
1136
                    ctrl = ARRAY['SELF', 'x7-A;#rzo']
1137
                WHERE nom_schema = NEW.nom_schema AND (
1138
                    oid_lecteur IS NULL
1139
                    OR NOT oid_lecteur = 0
1140
                    ) ;
1141
        ELSE
1142
            UPDATE z_asgard.gestion_schema_etr
1143
                SET oid_lecteur = quote_ident(NEW.lecteur)::regrole::oid,
1144
                    ctrl = ARRAY['SELF', 'x7-A;#rzo']
1145
                WHERE nom_schema = NEW.nom_schema AND (
1146
                    oid_lecteur IS NULL
1147
                    OR NOT oid_lecteur = quote_ident(NEW.lecteur)::regrole::oid
1148
                    ) ;
1149
        END IF ;
1150
    END IF ;
1151
    
1152
    ------ CREATION DU SCHEMA ------
1153
    -- on exclut au préalable les cas qui ne
1154
    -- correspondent pas à des créations, ainsi que les
1155
    -- remontées de l'event trigger sur CREATE SCHEMA,
1156
    -- car le schéma existe alors déjà
1157
    b_test := False ;
1158
    IF NOT NEW.creation OR NEW.ctrl[1] = 'CREATE'
1159
    THEN
1160
        b_test := True ;
1161
    ELSIF TG_OP = 'UPDATE'
1162
    THEN
1163
        IF OLD.creation
1164
        THEN
1165
            b_test := True ;
1166
        END IF ;
1167
    END IF ;
1168
    
1169
    IF NOT b_test
1170
    THEN
1171
        -- le schéma est créé s'il n'existe pas déjà (cas d'ajout
1172
        -- d'un schéma pré-existant qui n'était pas référencé dans
1173
        -- gestion_schema jusque-là), sinon on alerte juste
1174
        -- l'utilisateur
1175
        IF NOT NEW.nom_schema IN (SELECT nspname FROM pg_catalog.pg_namespace)
1176
        THEN
1177
            IF NOT has_database_privilege(current_database(), 'CREATE')
1178
                    OR NOT pg_has_role(NEW.producteur, 'USAGE')
1179
            THEN
1180
                -- si le rôle courant n'a pas les privilèges nécessaires pour
1181
                -- créer le schéma, on tente avec le rôle createur [de rôles]
1182
                -- pré-identifié, dont on sait au moins qu'il aura les
1183
                -- permissions nécessaires sur le rôle producteur - mais pas
1184
                -- s'il est habilité à créer des schémas
1185
                IF createur IS NOT NULL
1186
                THEN
1187
                    EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1188
                END IF ;
1189
                IF NOT has_database_privilege(current_database(), 'CREATE')
1190
                        OR NOT pg_has_role(NEW.producteur, 'USAGE')
1191
                THEN
1192
                    RAISE EXCEPTION 'TA9. Opération interdite. Vous n''êtes pas habilité à créer le schéma %.', NEW.nom_schema
1193
                        USING HINT = 'Être membre d''un rôle disposant du privilège CREATE sur la base de données est nécessaire pour créer des schémas.' ;
1194
                END IF ;
1195
            END IF ;
1196
            EXECUTE 'CREATE SCHEMA ' || quote_ident(NEW.nom_schema) || ' AUTHORIZATION ' || quote_ident(NEW.producteur) ;
1197
            EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1198
            RAISE NOTICE '... Le schéma % a été créé.', NEW.nom_schema ;
1199
        ELSE
1200
            RAISE NOTICE '(schéma % pré-existant)', NEW.nom_schema ;
1201
        END IF ;
1202
        -- récupération de l'OID du schéma
1203
        UPDATE z_asgard.gestion_schema_etr
1204
            SET oid_schema = quote_ident(NEW.nom_schema)::regnamespace::oid,
1205
                ctrl = ARRAY['SELF', 'x7-A;#rzo']
1206
            WHERE nom_schema = NEW.nom_schema AND (
1207
                oid_schema IS NULL
1208
                OR NOT oid_schema = quote_ident(NEW.nom_schema)::regnamespace::oid
1209
                ) ;   
1210
    END IF ;
1211
    
1212
    ------ APPLICATION DES DROITS DU PRODUCTEUR ------
1213
    -- comme précédemment pour la préparation du producteur,
1214
    -- on ne s'intéresse pas aux cas :
1215
    -- - d'un schéma qui n'a pas/plus vocation à exister
1216
    --   (creation vaut False) ;
1217
    -- - d'un schéma pré-existant dont le producteur ne change pas
1218
    --   ou dont le libellé a juste été nettoyé par le trigger
1219
    --   BEFORE ;
1220
    -- - d'un schéma qui vient d'être créé, car le producteur
1221
    --   sera déjà propriétaire du schéma et de son éventuel
1222
    --   contenu (INSERT ou remontée de l'event trigger
1223
    --   asgard_on_create_schema) ;
1224
    -- - de z_asgard_admin (pour permettre sa saisie initiale
1225
    --   dans la table de gestion, étant entendu qu'il est
1226
    --   impossible au trigger sur gestion_schema de lancer
1227
    --   un ALTER TABLE OWNER TO sur cette même table).
1228
    -- ils sont donc exclus au préalable
1229
    b_test := False ;
1230
    IF NOT NEW.creation
1231
            OR 'CLEAN producteur' = ANY(NEW.ctrl)
1232
            OR TG_OP = 'INSERT'
1233
            OR NEW.ctrl[1] = 'CREATE'
1234
            OR NEW.nom_schema = 'z_asgard_admin'
1235
    THEN
1236
        b_test := True ;
1237
    ELSIF TG_OP = 'UPDATE'
1238
    THEN
1239
        IF OLD.creation AND NEW.producteur = OLD.producteur
1240
        THEN
1241
            b_test := True ;
1242
        END IF ;
1243
    END IF ;
1244
    
1245
    IF NOT b_test
1246
    THEN
1247
        -- si besoin, on bascule sur le rôle createur. À ce stade,
1248
        -- il est garanti que soit l'utilisateur courant soit
1249
        -- createur (pour le cas d'un utilisateur courant
1250
        -- NOINHERIT) aura les privilèges nécessaires
1251
        IF NOT pg_has_role(NEW.producteur, 'USAGE')
1252
        THEN
1253
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1254
        ELSIF TG_OP = 'UPDATE'
1255
        THEN
1256
            IF NOT pg_has_role(a_producteur, 'USAGE')
1257
            THEN
1258
                EXECUTE 'SET ROLE ' || quote_ident(createur) ; 
1259
            END IF ;
1260
        END IF ;
1261
        
1262
        -- changements de propriétaires
1263
        IF (NEW.nom_schema, NEW.producteur)
1264
                IN (SELECT schema_name, schema_owner FROM information_schema.schemata)
1265
        THEN
1266
            -- si producteur est déjà propriétaire du schéma (cas d'une remontée de l'event trigger,
1267
            -- principalement), on ne change que les propriétaires des objets éventuels
1268
            IF quote_ident(NEW.nom_schema)::regnamespace::oid
1269
                    IN (SELECT refobjid FROM pg_catalog.pg_depend WHERE deptype = 'n')
1270
            THEN 
1271
                -- la commande n'est cependant lancée que s'il existe des dépendances de type
1272
                -- DEPENDENCY_NORMAL sur le schéma, ce qui est une condition nécessaire à
1273
                -- l'existence d'objets dans le schéma
1274
                RAISE NOTICE 'attribution de la propriété des objets au rôle producteur du schéma % :', NEW.nom_schema ;
1275
                SELECT z_asgard.asgard_admin_proprietaire(NEW.nom_schema, NEW.producteur, False)
1276
                    INTO n ;
1277
                IF n = 0
1278
                THEN
1279
                    RAISE NOTICE '> néant' ;
1280
                END IF ; 
1281
            END IF ;
1282
        ELSE
1283
            -- sinon schéma + objets
1284
            RAISE NOTICE 'attribution de la propriété du schéma et des objets au rôle producteur du schéma % :', NEW.nom_schema ;
1285
            PERFORM z_asgard.asgard_admin_proprietaire(NEW.nom_schema, NEW.producteur) ;
1286
        END IF ;
1287
        
1288
        EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1289
    END IF ;
1290
    
1291
    ------ APPLICATION DES DROITS DE L'EDITEUR ------
1292
    -- on ne s'intéresse pas aux cas :
1293
    -- - d'un schéma qui n'a pas/plus vocation à exister ;
1294
    -- - d'un schéma pré-existant dont l'éditeur ne change pas
1295
    --   (y compris pour rester vide) ou dont le libellé
1296
    --   a seulement été nettoyé par le trigger BEFORE.
1297
    -- ils sont donc exclus au préalable
1298
    b_test := False ;
1299
    IF NOT NEW.creation OR 'CLEAN editeur' = ANY(NEW.ctrl)
1300
    THEN
1301
        b_test := True ;
1302
    ELSIF TG_OP = 'UPDATE'
1303
    THEN
1304
        IF OLD.creation
1305
            AND coalesce(NEW.editeur, '') = coalesce(OLD.editeur, '')
1306
        THEN
1307
            b_test := True ;           
1308
        END IF ;
1309
    END IF ;
1310
    
1311
    IF NOT b_test
1312
    THEN
1313
        -- si besoin, on bascule sur le rôle createur. À ce stade,
1314
        -- il est garanti que soit l'utilisateur courant soit
1315
        -- createur (pour le cas d'un utilisateur courant
1316
        -- NOINHERIT) aura les privilèges nécessaires
1317
        IF NOT pg_has_role(NEW.producteur, 'USAGE')
1318
        THEN
1319
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1320
        END IF ;
1321
        
1322
        IF TG_OP = 'UPDATE'
1323
        THEN
1324
            -- la validité de OLD.editeur n'ayant
1325
            -- pas été contrôlée par le trigger BEFORE,
1326
            -- on le fait maintenant
1327
            IF OLD.editeur = 'public'
1328
            THEN
1329
                a_editeur := 'public' ;
1330
                -- récupération des modifications manuelles des
1331
                -- droits de OLD.editeur/public, grâce à la fonction
1332
                -- asgard_synthese_public
1333
                SELECT array_agg(commande) INTO l_commande
1334
                    FROM z_asgard.asgard_synthese_public(
1335
                        quote_ident(NEW.nom_schema)::regnamespace
1336
                        ) ;   
1337
            ELSE
1338
                SELECT rolname INTO a_editeur
1339
                    FROM pg_catalog.pg_roles
1340
                    WHERE pg_roles.oid = OLD.oid_editeur ;
1341
                IF FOUND
1342
                THEN
1343
                    -- récupération des modifications manuelles des
1344
                    -- droits de OLD.editeur, grâce à la fonction
1345
                    -- asgard_synthese_role
1346
                    SELECT array_agg(commande) INTO l_commande
1347
                        FROM z_asgard.asgard_synthese_role(
1348
                            quote_ident(NEW.nom_schema)::regnamespace,
1349
                            quote_ident(a_editeur)::regrole
1350
                            ) ;
1351
                END IF ;
1352
            END IF ;
1353
        END IF ;
1354

    
1355
        IF l_commande IS NOT NULL
1356
        -- transfert sur NEW.editeur des droits de
1357
        -- OLD.editeur, le cas échéant
1358
        THEN
1359
            IF NEW.editeur IS NOT NULL
1360
            THEN
1361
                RAISE NOTICE 'suppression et transfert vers le nouvel éditeur des privilèges de l''ancien éditeur du schéma % :', NEW.nom_schema ;
1362
            ELSE
1363
                RAISE NOTICE 'suppression des privilèges de l''ancien éditeur du schéma % :', NEW.nom_schema ;
1364
            END IF ;
1365
            FOREACH c IN ARRAY l_commande
1366
            LOOP
1367
                IF NEW.editeur IS NOT NULL
1368
                THEN
1369
                    EXECUTE format(c, NEW.editeur) ;
1370
                    RAISE NOTICE '> %', format(c, NEW.editeur) ;
1371
                END IF ;
1372
                IF c ~ '^GRANT'
1373
                THEN
1374
                    SELECT z_asgard.asgard_grant_to_revoke(c) INTO c_reverse ;
1375
                    EXECUTE format(c_reverse, a_editeur) ;
1376
                    RAISE NOTICE '> %', format(c_reverse, a_editeur) ;
1377
                END IF ;
1378
            END LOOP ;
1379
            
1380
        -- sinon, application des privilèges standards de l'éditeur
1381
        ELSIF NEW.editeur IS NOT NULL
1382
        THEN
1383
            RAISE NOTICE 'application des privilèges standards pour le rôle éditeur du schéma % :', NEW.nom_schema ;
1384
            
1385
            EXECUTE 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
1386
            RAISE NOTICE '> %', 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
1387
            
1388
            EXECUTE 'GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
1389
            RAISE NOTICE '> %', 'GRANT SELECT, UPDATE, DELETE, INSERT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
1390
            
1391
            EXECUTE 'GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
1392
            RAISE NOTICE '> %', 'GRANT SELECT, USAGE ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.editeur) ;
1393
            
1394
        END IF ;
1395
        
1396
        EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1397
    END IF ;
1398
    
1399
    ------ APPLICATION DES DROITS DU LECTEUR ------
1400
    -- on ne s'intéresse pas aux cas :
1401
    -- - d'un schéma qui n'a pas/plus vocation à exister ;
1402
    -- - d'un schéma pré-existant dont le lecteur ne change pas
1403
    --   (y compris pour rester vide) ou dont le libellé
1404
    --   a seulement été nettoyé par le trigger BEFORE.
1405
    -- ils sont donc exclus au préalable
1406
    b_test := False ;
1407
    l_commande := NULL ;
1408
    IF NOT NEW.creation OR 'CLEAN lecteur' = ANY(NEW.ctrl)
1409
    THEN
1410
        b_test := True ;
1411
    ELSIF TG_OP = 'UPDATE'
1412
    THEN
1413
        IF OLD.creation
1414
            AND coalesce(NEW.lecteur, '') = coalesce(OLD.lecteur, '')
1415
        THEN
1416
            b_test := True ;           
1417
        END IF ;
1418
    END IF ;
1419
    
1420
    IF NOT b_test
1421
    THEN
1422
        -- si besoin, on bascule sur le rôle createur. À ce stade,
1423
        -- il est garanti que soit l'utilisateur courant soit
1424
        -- createur (pour le cas d'un utilisateur courant
1425
        -- NOINHERIT) aura les privilèges nécessaires
1426
        IF NOT pg_has_role(NEW.producteur, 'USAGE')
1427
        THEN
1428
            EXECUTE 'SET ROLE ' || quote_ident(createur) ;
1429
        END IF ;
1430
        
1431
        IF TG_OP = 'UPDATE'
1432
        THEN
1433
            -- la validité de OLD.lecteur n'ayant
1434
            -- pas été contrôlée par le trigger BEFORE,
1435
            -- on le fait maintenant
1436
            IF OLD.lecteur = 'public'
1437
            THEN
1438
                a_lecteur := 'public' ;
1439
                -- récupération des modifications manuelles des
1440
                -- droits de OLD.lecteur/public, grâce à la fonction
1441
                -- asgard_synthese_public
1442
                SELECT array_agg(commande) INTO l_commande
1443
                    FROM z_asgard.asgard_synthese_public(
1444
                        quote_ident(NEW.nom_schema)::regnamespace
1445
                        ) ;   
1446
            ELSE
1447
                SELECT rolname INTO a_lecteur
1448
                    FROM pg_catalog.pg_roles
1449
                    WHERE pg_roles.oid = OLD.oid_lecteur ;
1450
                IF FOUND
1451
                THEN
1452
                    -- récupération des modifications manuelles des
1453
                    -- droits de OLD.lecteur, grâce à la fonction
1454
                    -- asgard_synthese_role
1455
                    SELECT array_agg(commande) INTO l_commande
1456
                        FROM z_asgard.asgard_synthese_role(
1457
                            quote_ident(NEW.nom_schema)::regnamespace,
1458
                            quote_ident(a_lecteur)::regrole
1459
                            ) ;
1460
                END IF ;
1461
            END IF ;
1462
        END IF ;
1463

    
1464
        IF l_commande IS NOT NULL
1465
        -- transfert sur NEW.lecteur des droits de
1466
        -- OLD.lecteur, le cas échéant
1467
        THEN
1468
            IF NEW.lecteur IS NOT NULL
1469
            THEN
1470
                RAISE NOTICE 'suppression et transfert vers le nouveau lecteur des privilèges de l''ancien lecteur du schéma % :', NEW.nom_schema ;
1471
            ELSE
1472
                RAISE NOTICE 'suppression des privilèges de l''ancien lecteur du schéma % :', NEW.nom_schema ;
1473
            END IF ;
1474
            FOREACH c IN ARRAY l_commande
1475
            LOOP
1476
                IF NEW.lecteur IS NOT NULL
1477
                THEN
1478
                    EXECUTE format(c, NEW.lecteur) ;
1479
                    RAISE NOTICE '> %', format(c, NEW.lecteur) ;
1480
                END IF ;
1481
                IF c ~ '^GRANT'
1482
                THEN
1483
                    SELECT z_asgard.asgard_grant_to_revoke(c) INTO c_reverse ;
1484
                    EXECUTE format(c_reverse, a_lecteur) ;
1485
                    RAISE NOTICE '> %', format(c_reverse, a_lecteur) ;
1486
                END IF ;
1487
            END LOOP ;
1488
            
1489
        -- sinon, application des privilèges standards du lecteur
1490
        ELSIF NEW.lecteur IS NOT NULL
1491
        THEN
1492
            RAISE NOTICE 'application des privilèges standards pour le rôle lecteur du schéma % :', NEW.nom_schema ;
1493
            
1494
            EXECUTE 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
1495
            RAISE NOTICE '> %', 'GRANT USAGE ON SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
1496
            
1497
            EXECUTE 'GRANT SELECT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
1498
            RAISE NOTICE '> %', 'GRANT SELECT ON ALL TABLES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
1499
            
1500
            EXECUTE 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
1501
            RAISE NOTICE '> %', 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA ' || quote_ident(NEW.nom_schema) || ' TO ' || quote_ident(NEW.lecteur) ;
1502
            
1503
        END IF ;
1504
        
1505
        EXECUTE 'SET ROLE ' || quote_ident(utilisateur) ;
1506
    END IF ;
1507
    
1508
	RETURN NULL ;
1509

    
1510
EXCEPTION WHEN OTHERS THEN
1511
    GET STACKED DIAGNOSTICS e_mssg = MESSAGE_TEXT,
1512
                            e_hint = PG_EXCEPTION_HINT,
1513
                            e_detl = PG_EXCEPTION_DETAIL ;
1514
    RAISE EXCEPTION 'TA0. Opération annulée. Anomalie lors de la traduction physique des modifications de la table de gestion.'
1515
         USING DETAIL = CASE WHEN e_detl = '' THEN e_mssg ELSE e_mssg || ' / ' || e_detl END,
1516
               HINT = e_hint ;    
1517
END
1518
$BODY$ ;
(4-4/6)