Surcharge de la fonction HSupprimeTout avec prise en charge des contraintes d'intégrité référentielle
Contexte Technique
184705
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
184706
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
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
184526
Contenu de la table commande initialisée avec le script
184527
Contenu de la table détail commande initialisée avec le script
La nouvelle fonction
184715
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 :
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
// 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) = 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
HSupprimeTout(Commande..nom)
Suppression complète du contenu du fichier DetailCommande
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 :
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 :ccool:
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.