forked from gleu/pgdocs_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
rules.xml
2048 lines (1750 loc) · 84.7 KB
/
rules.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<chapter id="rules">
<title>Système de règles</title>
<indexterm zone="rules">
<primary>règle</primary>
</indexterm>
<para>
Ce chapitre discute du système de règles dans
<productname>PostgreSQL</productname>. les systèmes de règles de
production sont simples conceptuellement mais il existe de nombreux points
subtils impliqués dans leur utilisation.
</para>
<para>
Certains autres systèmes de bases de données définissent des règles actives
pour la base de données, conservées habituellement en tant que procédures
stockées et déclencheurs. Avec <productname>PostgreSQL</productname>, elles
peuvent aussi être implémentées en utilisant des fonctions et des
déclencheurs.
</para>
<para>
Le système de règles (plus précisément, le système de règles de
réécriture de requêtes) est totalement différent des procédures stockées et
des déclencheurs. Il modifie les requêtes pour prendre en considération les
règles puis passe la requête modifiée au planificateur de requêtes pour
planification et exécution. Il est très puissant et peut être utilisé pour
beaucoup de choses comme des procédures en langage de requêtes, des vues et
des versions. Les fondations théoriques et la puissance de ce système de
règles sont aussi discutées dans <xref linkend="ston90b"/> et <xref
linkend="ong90"/>.
</para>
<sect1 id="querytree">
<title>Arbre de requêtes</title>
<indexterm zone="querytree">
<primary>arbre de requêtes</primary>
</indexterm>
<para>
Pour comprendre comment fonctionne le système de règles, il est nécessaire
de comprendre quand il est appelé et quelles sont ses entrées et sorties.
</para>
<para>
Le système de règles est situé entre l'analyseur et le planificateur. Il
prend la sortie de l'analyseur, un arbre de requête et les règles de
réécriture définies par l'utilisateur qui sont aussi des arbres de requêtes
avec quelques informations supplémentaires, et crée zéro ou plusieurs arbres
de requêtes comme résultat. Donc, son entrée et sortie sont toujours des
éléments que l'analyseur lui-même pourrait avoir produit et, du coup, tout
ce qu'il voit est représentable basiquement comme une instruction
<acronym>SQL</acronym>.
</para>
<para>
Maintenant, qu'est-ce qu'un arbre de requêtes ? C'est une
représentation interne d'une instruction <acronym>SQL</acronym> où les
parties qui le forment sont stockées séparément. Ces arbres de requêtes sont
affichables dans le journal de traces du serveur si vous avez configuré les
paramètres <varname>debug_print_parse</varname>,
<varname>debug_print_rewritten</varname>, ou
<varname>debug_print_plan</varname>. les actions de règles sont aussi
enregistrées comme arbres de requêtes dans le catalogue système
<structname>pg_rewrite</structname>. elles ne sont pas formatées comme la
sortie de traces mais elles contiennent exactement la même information.
</para>
<para>
Lire un arbre de requête brut requiert un peu d'expérience. Mais comme les
représentations <acronym>SQL</acronym> des arbres de requêtes sont
suffisantes pour comprendre le système de règles, ce chapitre ne
vous apprendra pas à les lire.
</para>
<para>
Lors de la lecture des représentations <acronym>SQL</acronym> des arbres de
requêtes dans ce chapitre, il est nécessaire d'être capable d'identifier les
morceaux cassés de l'instruction lorsqu'ils sont dans la structure de l'arbre
de requête. Les parties d'un arbre de requêtes sont
<variablelist>
<varlistentry>
<term>
le type de commande
</term>
<listitem>
<para>
C'est une simple valeur indiquant quelle commande
(<command>select</command>, <command>insert</command>,
<command>update</command>, <command>delete</command>)
l'arbre de requêtes produira.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
la table d'échelle
</term>
<listitem>
<indexterm><primary>table d'échelle</primary></indexterm>
<para>
La table d'échelle est une liste des relations utilisées dans la
requête. Dans une instruction <command>select</command>, ce sont les
relations données après le mot clé <literal>from</literal>.
</para>
<para>
Chaque entrée de la table d'échelle identifie une table ou une vue et
indique par quel nom elle est désignée dans les autres parties de la
requête. Dans l'arbre de requêtes, les entrées de la table d'échelle
sont référencées par des numéros plutôt que par des noms. Il
importe donc peu, ici, de savoir s'il y a des noms dupliqués comme cela
peut être le cas avec une instruction <acronym>SQL</acronym>. Cela peut
arriver après l'assemblage des tables d'échelle des règles.
Les exemples de ce chapitre ne sont pas confrontés à cette situation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
la relation résultat
</term>
<listitem>
<para>
C'est un index dans la table d'échelle qui identifie la relation où
iront les résultats de la requête.
</para>
<para>
Les requêtes <command>select</command> n'ont normalement pas de
relation résultat. Le cas spécial d'un <command>select into</command>
est pratiquement identique à un <command>create table</command> suivi
par un <literal>insert ... select</literal> et n'est pas discuté
séparément ici.
</para>
<para>
Pour les commandes <command>insert</command>, <command>update</command>
et <command>delete</command>, la relation de résultat est la table (ou
vue !) où les changements prennent effet.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
la liste cible
</term>
<listitem>
<indexterm><primary>liste cible</primary></indexterm>
<para>
La liste cible est une liste d'expressions définissant le résultat
d'une requête. Dans le cas d'un <command>select</command>, ces
expressions sont celles qui construisent la sortie finale de la requête.
Ils correspondent aux expressions entre les mots clés
<command>select</command> et <command>from</command>
(<literal>*</literal> est seulement une abréviation pour tous les noms
de colonnes d'une relation. Il est étendu par l'analyseur en colonnes
individuelles, pour que le système de règles ne le voit jamais).
</para>
<para>
Les commandes <command>delete</command> n'ont pas besoin d'une liste de
colonnes car elles ne produisent aucun résultat. En fait, le
planificateur ajoutera une entrée spéciale <acronym>ctid</acronym> pour aller
jusqu'à la liste de cibles mais c'est après le système de règles ;
pour le système de règles, ce sera discuté après ; pour le système
de règles, la liste de cibles est vide.
</para>
<para>
Pour les commandes <command>insert</command>, la liste cible décrit les
nouvelles lignes devant aller dans la relation résultat. Elle consiste
en des expressions de la clause <literal>values</literal> ou en celles de la
clause <command>select</command> dans <literal>insert ...
SELECT</literal>. la première étape du processus de réécriture ajoute
les entrées de la liste cible pour les colonnes n'ont affectées par la
commande originale mais ayant des valeurs par défaut. Toute colonne
restante (avec soit une valeur donnée soit une valeur par défaut) sera
remplie par le planificateur avec une expression NULL constante.
</para>
<para>
Pour les commandes <command>update</command>, la liste cible décrit les
nouvelles lignes remplaçant les anciennes. Dans le système des règles,
elle contient seulement les expressions de la partie <literal>set
colonne = expression</literal> de la commande. le planificateur gèrera
les colonnes manquantes en insérant des expressions qui copient les
valeurs provenant de l'ancienne ligne dans la nouvelle. et elle ajoutera
l'ancienne entrée <acronym>ctid</acronym> dans la nouvelle ligne comme pour le
<command>delete</command>.
</para>
<para>
Chaque entrée de la liste cible contient une expression qui peut être
une valeur constante, une variable pointant vers une colonne d'une des
relations de la table d'échelle, un paramètre ou un arbre d'expressions
réalisé à partir d'appels de fonctions, de constantes, de variables,
d'opérateurs, etc.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
la qualification
</term>
<listitem>
<para>
La qualification de la requête est une expression ressemblant à une de
celles contenues dans les entrées de la liste cible. La valeur résultant
de cette expression est un booléen indiquant si l'opération
(<command>insert</command>, <command>update</command>,
<command>delete</command> ou <command>select</command>) pour la ligne de
résultat final devrait être exécutée ou non. Elle correspond à la clause
<literal>where</literal> d'une instruction <acronym>SQL</acronym>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
l'arbre de jointure
</term>
<listitem>
<para>
L'arbre de jointure de la requête affiche la structure de la clause
<literal>from</literal>. pour une simple requête comme <literal>select ... from
a, b, c</literal>, l'arbre de jointure est une simple liste d'éléments
de <literal>from</literal> parce que nous sommes autorisés à les joindre dans
tout ordre. Mais quand des expressions <literal>join</literal>, et plus
particulièrement les jointures externes, sont utilisées, nous devons les
joindre dans l'ordre affiché par les jointures. Dans ce cas, l'arbre de
jointure affiche la structure des expressions <literal>join</literal>. les
restrictions associées avec ces clauses <literal>join</literal> particulières
(à partir d'expressions <literal>on</literal> ou <literal>using</literal>) sont
enregistrées comme des expressions de qualification attachées aux
nœuds
de l'arbre de jointure. Il s'avère agréable d'enregistrer l'expression
de haut niveau <literal>where</literal> comme une qualification attachée à
l'élément de l'arbre de jointure de haut niveau. Donc, réellement,
l'arbre de jointure représente à la fois les clauses <literal>from</literal> et
<literal>where</literal> d'un <command>select</command>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
le reste
</term>
<listitem>
<para>
Les autres parties de l'arbre de requête comme la clause <literal>order
BY</literal> n'ont pas d'intérêt ici. le système de règles substitue quelques
entrées lors de l'application des règles mais ceci n'a pas grand chose à
voir avec les fondamentaux du système de règles.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect1>
<sect1 id="rules-views">
<title>Vues et système de règles</title>
<indexterm zone="rules-views">
<primary>règle</primary>
<secondary>et vues</secondary>
</indexterm>
<indexterm zone="rules-views">
<primary>vue</primary>
<secondary>implémentation par les règles</secondary>
</indexterm>
<para>
Avec <productname>PostgreSQL</productname>, les vues sont implémentées en
utilisant le système de règles. En fait, il n'y a essentiellement pas de
différences entre
<programlisting>CREATE VIEW ma_vue AS SELECT * FROM ma_table;
</programlisting>
et ces deux commandes :
<programlisting>CREATE TABLE ma_vue (<replaceable>liste de colonnes identique à celle de ma_table</replaceable>);
CREATE RULE "_RETURN" AS ON SELECT TO ma_vue DO INSTEAD
SELECT * FROM ma_table;
</programlisting>
parce que c'est exactement ce que fait la commande <command>create
VIEW</command> en interne. Cela présente quelques effets de bord. L'un d'entre eux
est que l'information sur une vue dans les catalogues système
<productname>PostgreSQL</productname> est exactement la même que celle
d'une table. Donc, pour l'analyseur, il n'y a aucune différence entre une
table et une vue. Elles représentent la même chose : des relations.
</para>
<sect2 id="rules-select">
<title>Fonctionnement des règles <command>select</command></title>
<indexterm zone="rules-select">
<primary>règle</primary>
<secondary sortas="select">pour select</secondary>
</indexterm>
<para>
Les règles <literal>on select</literal> sont appliquées à toutes les requêtes comme
la dernière étape, même si la commande donnée est un
<command>insert</command>, <command>update</command> ou
<command>delete</command>. et ils ont des sémantiques différentes à partir
des règles sur les autres types de commandes dans le fait qu'elles modifient
l'arbre de requêtes en place au lieu d'en créer un nouveau. Donc, les règles
<command>select</command> sont décrites avant.
</para>
<para>
Actuellement, il n'existe qu'une action dans une règle <literal>on
SELECT</literal> et elle doit être une action <command>select</command>
inconditionnelle qui est <literal>instead</literal>. cette restriction était
requise pour rendre les règles assez sûres pour les ouvrir aux utilisateurs
ordinaires et cela restreint les règles <literal>on select</literal> à agir comme
des vues.
</para>
<para>
Pour ce chapitre, les exemples sont deux vues jointes réalisant quelques
calculs et quelques vues supplémentaires les utilisant à leur tour. Une
des deux premières vues est personnalisée plus tard en ajoutant des règles
pour des opérations <command>insert</command>, <command>update</command> et
<command>delete</command> de façon à ce que le résultat final sera une vue
qui se comporte comme une vraie table avec quelques fonctionnalités
magiques. Il n'existe pas un tel exemple pour commencer et ceci rend les
choses plus difficiles à obtenir. Mais il est mieux d'avoir un exemple
couvrant tous les points discutés étape par étape plutôt que plusieurs
exemples, rendant la compréhension plus difficile.
</para>
<para>
Pour cet exemple, nous avons besoin d'une petite fonction
<literal>min</literal> renvoyant la valeur la plus basse entre deux entiers.
Nous la créons ainsi :
<programlisting>CREATE FUNCTION min(integer, integer) RETURNS integer AS $$
SELECT CASE WHEN $1 < $2 THEN $1 ELSE $2 END
$$' LANGUAGE SQL STRICT;
</programlisting>
</para>
<para>
Les tables réelles dont nous avons besoin dans les deux
premières descriptions du système de règles sont les suivantes :
<programlisting>CREATE TABLE donnees_chaussure (
nom_chaussure text, -- clé primaire
dispo_chaussure integer, -- nombre de pairs disponibles
couleur_chaussure text, -- couleur de lacet préférée
long_min_chaussure real, -- longueur minimum du lacet
long_max_chaussure real, -- longueur maximum du lacet
unite_long_chaussure text -- unité de longueur
);
CREATE TABLE donnees_lacet (
nom_lacet text, -- clé primaire
dispo_lacet integer, -- nombre de pairs disponibles
couleur_lacet text, -- couleur du lacet
longueur_lacet real, -- longueur du lacet
unite_lacet text -- unité de longueur
);
CREATE TABLE unite (
nom_unite text, -- clé primaire
facteur_unite real -- facteur pour le transformer en cm
);
</programlisting>
Comme vous pouvez le constater, elles représentent les données d'un magasin
de chaussures.
</para>
<para>
Les vues sont créées avec :
<programlisting>CREATE VIEW chaussure AS
SELECT sh.nom_chaussure,
sh.dispo_chaussure,
sh.couleur_chaussure,
sh.long_min_chaussure,
sh.long_min_chaussure * un.facteur_unite AS long_min_chaussure_cm,
sh.long_max_chaussure,
sh.long_max_chaussure * un.facteur_unite AS long_max_chaussure_cm,
sh.unite_long_chaussure
FROM donnees_chaussure sh, unite un
WHERE sh.unite_long_chaussure = un.nom_unite;
CREATE VIEW lacet AS
SELECT s.nom_lacet,
s.dispo_lacet,
s.couleur_lacet,
s.longueur_lacet,
s.unite_lacet,
s.longueur_lacet * u.facteur_unite AS longueur_lacet_cm
FROM donnees_lacet s, unite u
WHERE s.unite_lacet = u.nom_unite;
CREATE VIEW chaussure_prete AS
SELECT rsh.nom_chaussure,
rsh.dispo_chaussure,
rsl.nom_lacet,
rsl.dispo_lacet,
min(rsh.dispo, rsl.dispo_lacet) AS total_avail
FROM chaussure rsh, lacet rsl
WHERE rsl.couleur_lacet = rsh.couleur
AND rsl.longueur_lacet_cm >= rsh.long_min_chaussure_cm
AND rsl.longueur_lacet_cm <= rsh.long_max_chaussure_cm;
</programlisting>
La commande <command>create view</command> pour la vue
<literal>lacet</literal> (qui est la plus simple que nous avons) écrira
une relation <literal>lacet</literal> et une entrée dans
<structname>pg_rewrite</structname> indiquant la présence d'une règle de
réécriture devant être appliquée à chaque fois que la relation
<literal>lacet</literal> est référencée dans une table de la requête.
La règle n'a aucune qualification de règle (discuté plus tard, avec les
règles autres que <command>select</command> car les règles <command>select</command> ne
le sont pas encore) et qu'il s'agit de <literal>instead</literal>. notez que les
qualifications de règles ne sont pas identiques aux qualifications de
requêtes. L'action de notre règle a une qualification de requête. L'action
de la règle a un arbre de requête qui est une copie de l'instruction
<command>select</command> dans la commande de création de la vue.
</para>
<note>
<para>
Les deux entrées supplémentaires de la table d'échelle pour <literal>new</literal>
et <literal>old</literal> (nommées <literal>*new*</literal> et <literal>*old*</literal> pour des
raisons historiques dans l'arbre de requête affiché) que vous pouvez voir
dans l'entrée de <structname>pg_rewrite</structname> ne sont d'aucun intérêt
pour les règles <command>select</command>.
</para>
</note>
<para>
Maintenant, nous remplissons <literal>unit</literal>,
<literal>donnees_chaussure</literal> et <literal>donnees_lacet</literal>,
puis nous lançons une requête simple sur une vue :
<programlisting>INSERT INTO unite VALUES ('cm', 1.0);
INSERT INTO unite VALUES ('m', 100.0);
INSERT INTO unite VALUES ('inch', 2.54);
INSERT INTO donnees_chaussure VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm');
INSERT INTO donnees_chaussure VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch');
INSERT INTO donnees_chaussure VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm');
INSERT INTO donnees_chaussure VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch');
INSERT INTO donnees_lacet VALUES ('sl1', 5, 'black', 80.0, 'cm');
INSERT INTO donnees_lacet VALUES ('sl2', 6, 'black', 100.0, 'cm');
INSERT INTO donnees_lacet VALUES ('sl3', 0, 'black', 35.0 , 'inch');
INSERT INTO donnees_lacet VALUES ('sl4', 8, 'black', 40.0 , 'inch');
INSERT INTO donnees_lacet VALUES ('sl5', 4, 'brown', 1.0 , 'm');
INSERT INTO donnees_lacet VALUES ('sl6', 0, 'brown', 0.9 , 'm');
INSERT INTO donnees_lacet VALUES ('sl7', 7, 'brown', 60 , 'cm');
INSERT INTO donnees_lacet VALUES ('sl8', 1, 'brown', 40 , 'inch');
SELECT * FROM lacet;
nom_lacet | dispo_lacet | couleur_lacet | longueur_lacet | unite_lacet | longueur_lacet_cm
-------------+-------------+---------------+----------------+-------------+------------------
sl1 | 5 | black | 80 | cm | 80
sl2 | 6 | black | 100 | cm | 100
sl7 | 7 | brown | 60 | cm | 60
sl3 | 0 | black | 35 | inch | 88.9
sl4 | 8 | black | 40 | inch | 101.6
sl8 | 1 | brown | 40 | inch | 101.6
sl5 | 4 | brown | 1 | m | 100
sl6 | 0 | brown | 0.9 | m | 90
(8 rows)
</programlisting>
</para>
<para>
C'est la requête <command>select</command> la plus simple que vous pouvez
lancer sur nos vues, donc nous prenons cette opportunité d'expliquer les
bases des règles de vues. <literal>select * from lacet</literal> a été
interprété par l'analyseur et a produit l'arbre de requête :
<programlisting>SELECT lacet.nom_lacet, lacet.dispo_lacet,
lacet.couleur_lacet, lacet.longueur_lacet,
lacet.unite_lacet, lacet.longueur_lacet_cm
FROM lacet lacet;
</programlisting>
et ceci est transmis au système de règles. Ce système traverse la table
d'échelle et vérifie s'il existe des règles pour chaque relation. Lors du
traitement d'une entrée de la table d'échelle pour
<literal>lacet</literal> (la seule jusqu'à maintenant), il trouve la
règle <literal>_return</literal> avec l'arbre de requête :
<programlisting>SELECT s.nom_lacet, s.dispo_lacet,
s.couleur_lacet, s.longueur_lacet, s.unite_lacet,
s.longueur_lacet * u.facteur_unite AS longueur_lacet_cm
FROM lacet *OLD*, lacet *NEW*,
donnees_lacet s, unit u
WHERE s.unite_lacet = u.nom_unite;
</programlisting>
</para>
<para>
Pour étendre la vue, la réécriture crée simplement une entrée de la table
d'échelle de sous-requête contenant l'arbre de requête de l'action de la règle
et substitue cette entrée avec l'original référencé dans la vue. L'arbre
d'échelle résultant de la réécriture est pratiquement identique à celui que
vous avez saisi :
<programlisting>SELECT lacet.nom_lacet, lacet.dispo_lacet,
lacet.couleur_lacet, lacet.longueur_lacet,
lacet.unite_lacet, lacet.longueur_lacet_cm
FROM (SELECT s.nom_lacet,
s.dispo_lacet,
s.couleur_lacet,
s.longueur_lacet,
s.unite_lacet,
s.longueur_lacet * u.facteur_unite AS longueur_lacet_cm
FROM donnees_lacet s, unit u
WHERE s.unite_lacet = u.nom_unite) lacet;
</programlisting>
Néanmoins, il y a une différence : la table d'échelle de la
sous-requête a deux entrées supplémentaires, <literal>lacet *old*</literal> et
<literal>lacet *new*</literal>. ces entrées ne participent pas directement dans
la requête car elles ne sont pas référencées par l'arbre de jointure de la
sous-requête ou par la liste cible. La réécriture les utilise pour
enregistrer l'information de vérification des droits d'accès qui étaient
présents à l'origine dans l'entrée de table d'échelle référencée par la
vue. De cette façon, l'exécution vérifiera toujours que l'utilisateur a les
bons droits pour accéder à la vue même s'il n'y a pas d'utilisation directe
de la vue dans la requête réécrite.
</para>
<para>
C'était la première règle appliquée. Le système de règles continuera de
vérifier les entrées restantes de la table d'échelle dans la requête
principale (dans cet exemple, il n'en existe pas plus), et il vérifiera
récursivement les entrées de la table d'échelle dans la sous-requête ajoutée
pour voir si une d'elle référence les vues. (Mais il n'étendra ni
<literal>*old*</literal> ni <literal>*new*</literal> — sinon nous aurions une récursion
infinie !) Dans cet exemple, il n'existe pas de règles de réécriture
pour <literal>donnees_lacet</literal> ou <literal>unit</literal>, donc la réécriture est
terminée et ce qui est ci-dessus est le résultat final donné au
planificateur.
</para>
<para>
Maintenant, nous voulons écrire une requête qui trouve les chaussures
en magasin dont nous avons les lacets correspondants
(couleur et longueur) et pour lesquels le nombre total de pairs
correspondants exactement est supérieur ou égal à deux.
<programlisting>SELECT * FROM chaussure_prete WHERE total_avail >= 2;
nom_chaussure | dispo | nom_lacet | dispo_lacet | total_avail
---------------+-------+-----------+-------------+-------------
sh1 | 2 | sl1 | 5 | 2
sh3 | 4 | sl7 | 7 | 4
(2 rows)
</programlisting>
</para>
<para>
Cette fois, la sortie de l'analyseur est l'arbre de requête :
<programlisting>SELECT chaussure_prete.nom_chaussure, chaussure_prete.dispo,
chaussure_prete.nom_lacet, chaussure_prete.dispo_lacet,
chaussure_prete.total_avail
FROM chaussure_prete chaussure_prete
WHERE chaussure_prete.total_avail >= 2;
</programlisting>
La première règle appliquée sera celle de la vue
<literal>chaussure_prete</literal> et cela résultera en cet arbre de
requête :
<programlisting>SELECT chaussure_prete.nom_chaussure, chaussure_prete.dispo,
chaussure_prete.nom_lacet, chaussure_prete.dispo_lacet,
chaussure_prete.total_avail
FROM (SELECT rsh.nom_chaussure,
rsh.dispo,
rsl.nom_lacet,
rsl.dispo_lacet,
min(rsh.dispo, rsl.dispo_lacet) AS total_avail
FROM chaussure rsh, lacet rsl
WHERE rsl.couleur_lacet = rsh.couleur
AND rsl.longueur_lacet_cm >= rsh.long_min_chaussure_cm
AND rsl.longueur_lacet_cm <= rsh.long_max_chaussure_cm) chaussure_prete
WHERE chaussure_prete.total_avail >= 2;
</programlisting>
De façon similaire, les règles pour <literal>chaussure</literal> et
<literal>lacet</literal> sont substituées dans la table d'échelle de
la sous-requête, amenant à l'arbre de requête final à trois niveaux :
<programlisting>SELECT chaussure_prete.nom_chaussure, chaussure_prete.dispo,
chaussure_prete.nom_lacet, chaussure_prete.dispo_lacet,
chaussure_prete.total_avail
FROM (SELECT rsh.nom_chaussure,
rsh.dispo,
rsl.nom_lacet,
rsl.dispo_lacet,
min(rsh.dispo, rsl.dispo_lacet) AS total_avail
FROM (SELECT sh.nom_chaussure,
sh.dispo,
sh.couleur,
sh.long_min_chaussure,
sh.long_min_chaussure * un.facteur_unite AS long_min_chaussure_cm,
sh.long_max_chaussure,
sh.long_max_chaussure * un.facteur_unite AS long_max_chaussure_cm,
sh.unite_long_chaussure
FROM donnees_chaussure sh, unit un
WHERE sh.unite_long_chaussure = un.nom_unite) rsh,
(SELECT s.nom_lacet,
s.dispo_lacet,
s.couleur_lacet,
s.longueur_lacet,
s.unite_lacet,
s.longueur_lacet * u.facteur_unite AS longueur_lacet_cm
FROM donnees_lacet s, unit u
WHERE s.unite_lacet = u.nom_unite) rsl
WHERE rsl.couleur_lacet = rsh.couleur
AND rsl.longueur_lacet_cm >= rsh.long_min_chaussure_cm
AND rsl.longueur_lacet_cm <= rsh.long_max_chaussure_cm) chaussure_prete
WHERE chaussure_prete.total_avail > 2;
</programlisting>
</para>
<para>
Il s'avère que le planificateur réduira cet arbre en un arbre de requête
à deux niveaux : les commandes <command>select</command> du bas seront
<quote>remontées</quote> dans le <command>select</command> du milieu car il
n'est pas nécessaire de les traiter séparément. Mais le
<command>select</command> du milieu restera séparé du haut car il contient
des fonctions d'agrégat. Si nous les avions monté, cela aurait modifié le
comportement du <command>select</command> de haut niveau, ce qui n'est pas
ce que nous voulons. Néanmoins, réduire l'arbre de requête est une
optimisation qui ne concerne pas le système de réécriture.
</para>
</sect2>
<sect2>
<title>Règles de vue dans des instructions autres que <command>select</command></title>
<para>
Deux détails de l'arbre de requête n'ont pas été abordés dans la
description des règles de vue ci-dessus. Ce sont le type de commande et le
relation résultante. En fait, les règles de vue n'ont pas besoin de cette
information.
</para>
<para>
Il existe seulement quelques différences entre un arbre de requête pour un
<command>select</command> et un pour une autre commande. de façon évidente,
ils ont un type de commande différent et pour une commande autre qu'
un <command>select</command>, la relation résultante pointe vers l'entrée de
table d'échelle où le résultat devrait arriver. Tout le reste est absolument
identique. Donc, avec deux tables <literal>t1</literal> et <literal>t2</literal> avec les
colonnes <literal>a</literal> et <literal>b</literal>, les arbres de requêtes pour les
deux commandes :
<programlisting>SELECT t2.b FROM t1, t2 WHERE t1.a = t2.a;
UPDATE t1 SET b = t2.b FROM t2 WHERE t1.a = t2.a;
</programlisting>
sont pratiquement identiques. En particulier :
<itemizedlist>
<listitem>
<para>
Les tables d'échelle contiennent des entrées pour les tables
<literal>t1</literal> et <literal>t2</literal>.
</para>
</listitem>
<listitem>
<para>
Les listes cibles contiennent une variable pointant vers la colonne
<literal>b</literal> de l'entrée de la table d'échelle pour la table
<literal>t2</literal>.
</para>
</listitem>
<listitem>
<para>
Les expressions de qualification comparent les colonnes
<literal>a</literal> des deux entrées de table d'échelle pour une égalité.
</para>
</listitem>
<listitem>
<para>
Les arbres de jointure affichent une jointure simple entre
<literal>t1</literal> et <literal>t2</literal>.
</para>
</listitem>
</itemizedlist>
</para>
<para>
La conséquence est que les deux arbres de requête résultent en des plans
d'exécution similaires : ce sont tous les deux des jointures sur les
deux tables. Pour l'<command>update</command>, les colonnes manquantes
de <literal>t1</literal> sont ajoutées à la liste cible par le planificateur et
l'arbre de requête final sera lu de cette façon :
<programlisting>UPDATE t1 SET a = t1.a, b = t2.b FROM t2 WHERE t1.a = t2.a;
</programlisting>
et, du coup, l'exécuteur lancé sur la jointure produira exactement le même
résultat qu'un :
<programlisting>SELECT t1.a, t2.b FROM t1, t2 WHERE t1.a = t2.a;
</programlisting>
le ferait. Mais il y a un petit problème avec
<command>update</command> : l'exécuteur ne fait pas attention au but du
résultat de la jointure. Il produit simplement un ensemble de lignes
composant le résultat. La différence entre une commande
<command>select</command> et une commande <command>update</command> est
gérée par celui qui a appelé l'exécuteur. L'appelant sait toujours (en
regardant dans l'arbre de requêtes) qu'il s'agit d'un
<command>update</command>, et il sait que le résultat ira dans la table
<literal>t1</literal>. mais quelles lignes disponibles maintenant seront remplacées
par les nouvelles lignes ?
</para>
<para>
Pour résoudre ce problème, une autre entrée est ajoutée dans la liste
cible de l'<command>update</command> (et aussi dans les instructions
<command>delete</command>) : l'identifiant actuel du tuple
(<acronym>ctid</acronym>, acronyme de <foreignphrase>current tuple
ID</foreignphrase>).<indexterm><primary>ctid</primary></indexterm> cette colonne système
contient le numéro de bloc du fichier et la position dans le bloc pour
cette ligne. Connaissant la table, le <acronym>ctid</acronym> peut être utilisé
pour récupérer la ligne originale de <literal>t1</literal> à mettre à jour. après
avoir ajouté le <acronym>ctid</acronym> dans la liste cible, la requête ressemble à
ceci :
<programlisting>SELECT t1.a, t2.b, t1.ctid FROM t1, t2 WHERE t1.a = t2.a;
</programlisting>
Maintenant, un autre détail de <productname>PostgreSQL</productname> entre
en jeu. Les anciennes lignes de la table ne sont pas surchargées et cela
explique pourquoi <command>rollback</command> est rapide. avec un
<command>update</command>, la nouvelle ligne résultat est insérée dans la
table (après avoir enlevé le <acronym>ctid</acronym>) et, dans le nouvel en-tête de
ligne de l'ancienne ligne, vers où pointe le <acronym>ctid</acronym>, les entrées
<literal>cmax</literal> et <literal>xmax</literal> sont configurées par le compteur de
commande actuel et par l'identifiant de transaction actuel. Du coup,
l'ancienne ligne est cachée et, après validation de la transaction, le
nettoyeur (vacuum) peut réellement la supprimer.
</para>
<para>
Connaissant tout ceci, nous pouvons simplement appliquer les règles de vues
de la même façon que toute autre commande. Il n'y a pas de différence.
</para>
</sect2>
<sect2>
<title>Puissance des vues dans <productname>PostgreSQL</productname></title>
<para>
L'exemple ci-dessus démontre l'incorporation des définitions de
vues par le système de règles dans l'arbre de requête original. Dans le
deuxième exemple, un simple <command>select</command> d'une vue a créé un
arbre de requête final qui est une jointure de quatre tables
(<literal>unit</literal> a été utilisé deux fois avec des noms différents).
</para>
<para>
Le bénéfice de l'implémentation des vues avec le système de règles est que
le planificateur a toute l'information sur les tables à parcourir et sur
les relations entre ces tables et les qualifications restrictives à partir
des vues et les qualifications à partir de la requête originale dans un
seule arbre de requête. Et c'est toujours la situation quand la requête
originale est déjà une jointure sur des vues. Le planificateur doit décider
du meilleur chemin pour exécuter la requête et plus le planificateur a
d'informations, meilleure sera la décision. Le système de règles implémenté
dans <productname>PostgreSQL</productname> s'en assure, c'est toute
l'information disponible sur la requête à ce moment.
</para>
</sect2>
<sect2 id="rules-views-update">
<title>Mise à jour d'une vue</title>
<para>
Qu'arrive-t'il si une vue est nommée comme la relation cible d'un
<command>insert</command>, <command>update</command> ou
<command>delete</command> ? après avoir réalisées les substitutions
décrites ci-dessus, nous aurons un arbre de requête dans lequel la relation
résultante pointe vers une entrée de la table d'échelle de la sous-requête.
Ceci ne fonctionnera pas car la réécriture renvoie une erreur si elle
aperçoit qu'elle a produit une telle chose.
</para>
<para>
Pour modifier ceci, nous pouvons définir des règles modifiant le
comportement de ce type de commandes. C'est donc le thème de la prochaine
section.
</para>
</sect2>
</sect1>
<sect1 id="rules-update">
<title>Règles sur <command>insert</command>, <command>update</command> et
<command>delete</command></title>
<indexterm zone="rules-update">
<primary>règle</primary>
<secondary sortas="insert">pour insert</secondary>
</indexterm>
<indexterm zone="rules-update">
<primary>règle</primary>
<secondary sortas="update">pour update</secondary>
</indexterm>
<indexterm zone="rules-update">
<primary>règle</primary>
<secondary sortas="delete">pour delete</secondary>
</indexterm>
<para>
Les règles définies sur <command>insert</command>, <command>update</command>
et <command>delete</command> sont significativement différentes des règles de
vue décrites dans la section précédente. Tout d'abord, leur commande
<command>create rule</command> permet plus de choses :
<itemizedlist>
<listitem>
<para>
Elles peuvent n'avoir aucune action.
</para>
</listitem>
<listitem>
<para>
Elles peuvent avoir plusieurs actions.
</para>
</listitem>
<listitem>
<para>
Elles peuvent être de type <literal>instead</literal> ou <literal>also</literal>
(valeur par défaut).
</para>
</listitem>
<listitem>
<para>
Les pseudo relations <literal>new</literal> et <literal>old</literal> deviennent
utiles.
</para>
</listitem>
<listitem>
<para>
Elles peuvent avoir des qualifications de règles.
</para>
</listitem>
</itemizedlist>
Ensuite, elles ne modifient pas l'arbre de requête en place. À la place,
elles créent de nouveaux arbres de requêtes et peuvent abandonner
l'original.
</para>
<sect2>
<title>Fonctionnement des règles de mise à jour</title>
<para>
Gardez en tête la syntaxe :
<programlisting>CREATE [ OR REPLACE ] RULE <replaceable class="parameter">nom</replaceable> as on <replaceable class="parameter">evenement</replaceable>
TO <replaceable class="parameter">table</replaceable> [ where <replaceable class="parameter">condition</replaceable> ]
DO [ ALSO | INSTEAD ] { NOTHING | <replaceable class="parameter">commande</replaceable> | ( <replaceable class="parameter">commande</replaceable> ; <replaceable class="parameter">commande</replaceable> ... ) }
</programlisting>
Dans la suite, <firstterm>règles de mise à jour</firstterm> signifie les règles qui
sont définies sur <command>insert</command>, <command>update</command> ou
<command>delete</command>.
</para>
<para>
Les règles de mise à jour sont appliquées par le système de règles lorsque
la relation résultante et le type de commande d'un arbre de requête sont
égaux pour l'objet et l'événement donné dans la commande <command>create
RULE</command>. pour les règles de mise à jour, le système de règles crée
une liste d'arbres de requêtes. Initialement, la liste d'arbres de requêtes
est vide. Il peut y avoir aucune (mot clé <literal>nothing</literal>), une ou
plusieurs actions. Pour simplifier, nous verrons une règle avec une action.
Cette règle peut avoir une qualification et peut être de type
<literal>instead</literal> ou <literal>also</literal> (valeur par défaut).
</para>
<para>
Qu'est-ce qu'une qualification de règle ? C'est une restriction
indiquant le moment où doivent être réalisés les actions de la règle. Cette
qualification peut seulement référencer les pseudo relations <literal>new</literal>
et/ou <literal>old</literal>, qui représentent basiquement la relation qui a été
donné comme objet (mais avec une signification spéciale).
</para>
<para>
Donc, nous avons trois cas qui produisent les arbres de requêtes suivants
pour une règle à une seule action.
<variablelist>
<varlistentry>
<term>sans qualification avec soit <literal>ALSO</literal> soit <literal>INSTEAD</literal></term>
<listitem>
<para>
l'arbre de requête à partir de l'action de la règle avec l'ajout de la
qualification de l'arbre de requête original
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>qualification donnée et <literal>also</literal></term>
<listitem>
<para>
l'arbre de requête à partir de l'action de la règle avec l'ajout de la
qualification de la règle et de la qualification de l'arbre de requête
original
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>qualification donnée avec <literal>instead</literal></term>
<listitem>
<para>
l'arbre de requête à partir de l'action de la règle avec la
qualification de la requête et la qualification de l'arbre de requête
original ; et l'ajout de l'arbre de requête original avec la
qualification inverse de la règle
</para>
</listitem>
</varlistentry>
</variablelist>
Enfin, si la règle est <literal>also</literal>, l'arbre de requête
original est ajouté à la liste. Comme seules les règles qualifiées
<literal>instead</literal> ont déjà ajouté l'arbre de requête original, nous
finissons avec un ou deux arbres de requête en sortie pour une règle avec
une action.
</para>
<para>
Pour les règles <literal>on insert</literal>, la requête originale (si elle n'est
pas supprimée par <literal>instead</literal>) est réalisée avant toute action
ajoutée par les règles. Ceci permet aux actions de voir les lignes insérées.
Mais pour les règles <literal>on update</literal> et <literal>on delete</literal>, la
requête originale est réalisée après les actions ajoutées par les règles.
Ceci nous assure que les actions pourront voir les lignes à mettre à jour ou
à supprimer ; sinon, les actions pourraient ne rien faire parce
qu'elles ne trouvent aucune ligne correspondant à leurs qualifications.
</para>
<para>
Les arbres de requêtes générés à partir des actions de règles sont
envoyés de nouveau dans le système de réécriture et peut-être que d'autres
règles seront appliquées résultant en plus ou moins d'arbres de requêtes.
Donc, les actions d'une règle doivent avoir soit un type de commande
différent soit une relation résultante différente de celle où la règle
elle-même est active, sinon ce processus récursif se terminera dans une
boucle infinie. (L'expansion récursive d'une règle sera détectée et
rapportée comme une erreur.)
</para>
<para>
Les arbres de requête trouvés dans les actions du catalogue système
<structname>pg_rewrite</structname> sont seulement des modèles. comme ils