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$ ;
|