WinDev : HsupprimeTout et la gestion des contraintes d'intégrité référentielle
Un billet par dsr57

Le , par dsr57, Rédacteur/Modérateur
Surcharge de la fonction HSupprimeTout avec prise en charge des contraintes d'intégrité référentielle

Contexte Technique




Dans un premier billet WinDev : HsupprimeTout et les contraintes d'intégrité référentielle en cascade, nous constations avec la confirmation du support technique la non prise en charge les contraintes d'intégrité référentielle définies dans l'analyse par la fonction HsupprimteTout .

Dans le but de palier à ce manque, ce bogue, cet oublie (chacun le qualifiera comme il le souhaite), j'ai développé une fonction qui prend en charge les contraintes d'intégrité.

Version de WinDev : 20 et antérieures
Programmation : surcharge, exceptions.

Jeu d'essai




Avant de mettre le code de la fonction développée, je met à disposition le jeu d'essai qui m'a permis de faire les tests.

Commande


Nom du fichier : commande.FIC

Clé Nom Libellé Type Taille
X ID Identifiant de la commande Id. automatique 4
Date Date de la commande Date 8
Client Nom du client Texte 50
DetailCommande

Nom du fichier : DetailCommande.FIC

Clé Nom Libellé Type Taille
X ID Identifiant du détail de la commande Id. automatique 4
IDCommande Identifiant de la commande Numérique 4
IdArticle Identifiant de l'article Numérique 4
Une relation entre les deux table est créée, cell-ci varie selon de vos contextes technique et focntionnel

Script d'initialisation

Ci-dessous le script d'initialisation des tables

Code WinDev : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  
HCréationSiInexistant(Commande) 
HCréationSiInexistant(CommandeDetail) 
  
WL.HSupprimeTout(Commande) 
WL.HSupprimeTout(CommandeDetail) 
  
POUR li_i=1 _A_  10  
	Commande.Date=DateDuJour() 
	Commande.Numero=NumériqueVersChaîne(li_i,"010d") 
	Commande.Client="ClientTest_"+li_i 
  
	HAjoute(Commande) 
  
	POUR li_j=li_i _A_ li_i+3  
		CommandeDetail.IDCommande=Commande.ID 
		CommandeDetail.IdArticle=li_j 
		HAjoute(CommandeDetail) 
	FIN 
  
FIN

Contenu de la table commande initialisée avec le script


Contenu de la table détail commande initialisée avec le script

La nouvelle fonction




Le nom de la fonctions a été créé en conservant la même syntaxe que celles proposées par PC-Soft
Nom : HSupprimeTout
Cette nouvelle fonction surcharge l'existante, WinDev affichera des informations a chaque appel de celle-ci.

Exemple d'information affichée :
Code Windev : Sélectionner tout
1
2
Info : La procédure 'HSupprimeTout' surcharge la fonction du WLangage de même nom. 
Fen_TestPROCEDURE.HSupprimeTout, Procédure locale, ligne 13, colonne 11

La fonction gère les contraintes d'intégrité :
  • interdire la supression qui a au moins un enregistrement
  • supprimer l'enregistrement et tous ces détails
  • supprimer l'enregistrement et affecter une valeur par défaut aux détails

et la gestion des cardinalités minimums

Code WinDev : Sélectionner tout
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
// Résumé : Supprime tout le contenu d'un fichier en prenant en compte les contraintes d'intégrité 
// Syntaxe : 
//HsupprimeTout(ps_nomFichier est une chaine) 
// 
// Paramètres : 
//	ps_nomFichier : Nom du fichier de données 
// Valeur de retour : 
// 	booléen : vrai si le contenu du fichier a été supprimer. 
// 
// Exemple : 
// HsupprimeTout(Commande..Nom) 
// 
  
PROCEDURE HsupprimeTout(ps_nomFichier est une chaîne) 
  
//----->Declaration des variables 
ls_LstLiaison,ls_nomLiaison,ls_FichierClePrimaire, ls_clePrimaire, ls_fichierCleEtrangere, ls_CleEtrangere, ls_regleSupression, ls_cardinaliteFichierPrimaire, ls_cardinaliteFichierSecondaire est une chaîne 
ls_LstValeurLiees,ls_nomFichierParametre, ls_requete, ls_nomReq  est une chaîne 
ls_MessageException1, ls_MessageException2, ls_MessageException3, ls_MessageException4 est une chaîne 
lt_BufferRequete est un tableau de chaîne 
  
//----->Initialisation des variables Message Exception 
ls_MessageException1=[ 
La  source de données <%1> n'est pas initialisée. 
     - S'il s'agit d'un fichier de données, le fichier n'a pas été trouvé dans l'analyse ou n'a pas été décrit avec les fonctions HDéclare / HDéclareExterne. 
     - S'il s'agit d'une requête ou d'une vue, l'exécution a peut-être échoué.  
Pour récupérer l'erreur correspondante, testez le résultat des fonctions HExécuteRequête / HExécuteRequêteSQL / HCréeVue. 
] 
ls_MessageException2="Le champ '%1' n'est pas de type Fichier de données" 
ls_MessageException3=[ 
	Erreur d'intégrité. 
	L'application de la fonction aurait entraîné le non-respect de la contrainte d'intégrité référentielle 'restrict' entre les rubriques <%1.%2> (clé primaire) ET <%3.%4> (clé étrangère). 
] 
ls_MessageException4=[ 
Erreur d'intégrité. 
Les cardinalités côté clé primaire (%1) entre les rubriques <%2.%3> et <%4.%5> ne sont pas respectées. 
] 
  
//----->Initialisation des variables 
ls_nomFichierParametre=ps_nomFichier 
  
//----->Test si la variable passé en paramètre existe en tantque fichier de données 
QUAND EXCEPTION DANS 
	HLitPremier(ls_nomFichierParametre)	 
FAIRE 
	ExceptionDéclenche(1,ChaîneConstruit(ls_MessageException1,ls_nomFichierParametre)) 
FIN 
  
//----->Test du type du pamètre 
SI PAS ({ls_nomFichierParametre}..Type _DANS_ (hFichierAS400, hFichierAutre, hFichierClientServeur, hFichierHF5, hFichierMySQL, hFichierNormal, hFichierOLEDB, hFichierOracle, hFichierOracleLite, hFichierPostgreSQL, hFichierProgress)) ALORS 
	ExceptionDéclenche(2,ChaîneConstruit(ls_MessageException2,ls_nomFichierParametre)) 
FIN 
  
//----->Rcp des liaisons liés au fichier passé en paramètre 
ls_LstLiaison=HListeLiaison(ls_nomFichierParametre,hLstDétail)  
  
//----->Boucle sur toutes les liaisons 
POUR TOUTE CHAÎNE ls_liaison DE ls_LstLiaison SEPAREE PAR RC 
  
	//----->Récupération des caractéristiques de la liaison 
	ls_nomLiaison=ExtraitChaîne(ls_liaison,1,TAB,DepuisDébut) 
	ls_FichierClePrimaire=ExtraitChaîne(ls_liaison,2,TAB,DepuisDébut) 
	ls_clePrimaire=ExtraitChaîne(ls_liaison,3,TAB,DepuisDébut) 
	ls_cardinaliteFichierPrimaire=ExtraitChaîne(ls_liaison,4,TAB,DepuisDébut) 
	ls_fichierCleEtrangere=ExtraitChaîne(ls_liaison,5,TAB,DepuisDébut) 
	ls_CleEtrangere=ExtraitChaîne(ls_liaison,6,TAB,DepuisDébut) 
	ls_cardinaliteFichierSecondaire=ExtraitChaîne(ls_liaison,7,TAB,DepuisDébut) 
	ls_regleSupression=ExtraitChaîne(ls_liaison,9,TAB,DepuisDébut) 
  
	SI ls_FichierClePrimaire = ls_nomFichierParametre ALORS 
  
		SI Val(ExtraitChaîne(ls_cardinaliteFichierSecondaire,1,",",DepuisDébut)) > 0  ALORS 
			ExceptionDéclenche(3,ChaîneConstruit(ls_MessageException4,ls_cardinaliteFichierSecondaire,ls_fichierCleEtrangere,ls_CleEtrangere,ls_FichierClePrimaire,ls_clePrimaire)) 
		FIN 
  
		//----->Initialisation des variables 
		ls_LstValeurLiees="" 
  
		//----->Req : recherche des valeurs à supprimer dans le fichier source 
		ls_requete="SELECT DISTINCT  "+ls_clePrimaire+" FROM "+ls_FichierClePrimaire			 
		ls_nomReq="ReqSelectValeurASupprimer" 
  
		//----->Req : Exécution et parcours de la requete 
		SI PAS HExécuteRequêteSQL(ls_nomReq,ls_requete) ALORS 
			HFerme(ls_nomReq) 
			RENVOYER Faux 
		SINON 
			HLitPremier(ls_nomReq)  
			TANTQUE PAS HEnDehors(ls_nomReq)  
				ls_LstValeurLiees+=HRécupèreRubrique(ls_nomReq,1)+"','" 
				HLitSuivant(ls_nomReq) 
			FIN 
			HLibèreRequête(ls_nomReq) 
			SI ls_LstValeurLiees <> "" ALORS 
				ls_LstValeurLiees="'"+Tronque(ls_LstValeurLiees,3,nombreDeCaractèresASupprimer)+"'" 
			FIN 
		FIN 
  
		//----->Test si des valeurs sont à suppimer 
		SI ls_LstValeurLiees<>"" ALORS 
  
			//----->Test selon la regle de suppression 
			SELON ls_regleSupression 
  
				CAS hIntégritéInterdite 
					//---->Req : Test si des enregistrements sont reliés au fichier source par le svaleurs à supprimer 
					ls_requete="SELECT COUNT(*) FROM "+ls_fichierCleEtrangere+" WHERE "+ls_CleEtrangere+" IN ("+ls_LstValeurLiees+")" 
					ls_nomReq="reqUpdateDetail" 
  
					//----->Req : Exécution et parcours 
					SI PAS HExécuteRequêteSQL(ls_nomReq,ls_requete) ALORS 
						HFerme(ls_nomReq) 
						RENVOYER Faux 
					SINON 
						HLitPremier(ls_nomReq)  
						//----->Test si des enregistrements sont reliés : on ne fait rien 
						SI HRécupèreRubrique(ls_nomReq,1) <> 0 ALORS 
							HLibèreRequête(ls_nomReq) 
							ExceptionDéclenche(3,ChaîneConstruit(ls_MessageException3,ls_FichierClePrimaire,ls_clePrimaire,ls_fichierCleEtrangere,ls_CleEtrangere)) 
						FIN 
						HLibèreRequête(ls_nomReq) 
					FIN 
  
				CAS hIntégritéCascade 
					//----->Req : Ajout dans le buffer la requête de suppression des enregistrements reliés au fichier source par le svaleurs à supprimer 
					TableauAjoute(lt_BufferRequete,"DELETE FROM  "+ls_fichierCleEtrangere+" WHERE "+ls_CleEtrangere+" IN ("+ls_LstValeurLiees+")") 
  
				CAS hIntégritéValeurDéfaut 
					//----->Req : Ajout dans le buffer la requête de maj des enregistrements par la valeur par défaut reliés au fichier source par le svaleurs à supprimer 
					TableauAjoute(lt_BufferRequete,"UPDATE  "+ls_fichierCleEtrangere+" SET "+ls_CleEtrangere+"='"+{ls_fichierCleEtrangere+"."+ls_CleEtrangere,indRubrique}..ValeurParDéfaut+"' WHERE "+ls_CleEtrangere+" IN ("+ls_LstValeurLiees+")") 
  
				AUTRE CAS 
  
			FIN 
		FIN 
	FIN 
FIN 
  
//----->Exécution des requêtes pour mettre en place les contraintes d'intégrité 
POUR li_i=1 _A_ TableauInfo(lt_BufferRequete,tiNombreTotal) 
	SI HExécuteRequêteSQL("Req"+li_i,lt_BufferRequete[li_i]) = Faux ALORS 
		ExceptionDéclenche(3,HErreurInfo(hErrComplet)) 
	FIN 
FIN 
  
SI WL.HSupprimeTout(ps_nomFichier) ALORS 
	RENVOYER Vrai 
SINON 
	RENVOYER Faux	 
FIN

j'ai effectué différents tests en variant les contraintes d'intégrité avec le code suivant :

Suppression complète du contenu du fichier commande
Code Windev : Sélectionner tout
HSupprimeTout(Commande..nom)

Suppression complète du contenu du fichier DetailCommande
Code Windev : Sélectionner tout
HSupprimeTout(DetailCommande..nom)


Les exceptions

Comme vous avez pu le constater en lisant le code source des fonctions, celles-ci déclenchent des exceptions en cas de problème. Trois types d'exceptions sont gérées :

Code Message
1 La source de données <%1> n'est pas initialisée.
- S'il s'agit d'un fichier de données, le fichier n'a pas été trouvé dans l'analyse ou n'a pas été décrit avec les fonctions HDéclare / HDéclareExterne.
- S'il s'agit d'une requête ou d'une vue, l'exécution a peut-être échoué.
Pour récupérer l'erreur correspondante, testez le résultat des fonctions HExécuteRequête / HExécuteRequêteSQL / HCréeVue.
2 Le champ '%1' n'est pas de type fichier de données
3 Erreur d'intégrité.
Les cardinalités côté clé primaire (%1) entre les rubriques <%2.%3> et <%4.%5> ne sont pas respectées

ou

Erreur d'intégrité.
L'application de la fonction aurait entraîné le non-respect de la contrainte d'intégrité référentielle 'restrict' entre les rubriques <%1.%2> (clé primaire) ET <%3.%4> (clé étrangère)
Exemple de code avec la gestion des exceptions :

Code WinDev : Sélectionner tout
1
2
3
4
5
QUAND EXCEPTION DANS  
	HSupprimeTout(Commande..nom) 
FAIRE 
	//Gestion des exceptions 
FIN

Conclusion

Ce billet propose une surcharge de la fonction HSupprimeTout pour prendre en compte les contraintes d'intégrité, celle-ci ne gère qu'un seul niveau. Si le fichier de destination est lié lui aussi à des containtes, celles-ci ne sont pas prises en compte, cela pourrait faire office d'une verison 2 ...

Si vous avez des remarques, des suggestions, des remotées de BUG, ... n'hésitez pas

Bon dev


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :
Offres d'emploi IT
Recherche développeur WINDEV, WEBDEV, WINMOBILE
Business Bagages - Provence Alpes Côte d'Azur - Aix en provence
DEVELOPEUR WINDEV ERP
COGNITIC - Ile de France - TOULOUSE
Chef de projet windev H/F
Solutec Paris - Ile de France - Paris (75000)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique WinDev