PostgreSQL La base de donnees la plus sophistiquee au monde.

Forums PostgreSQL.fr

Le forum officiel de la communauté francophone de PostgreSQL

Vous n'êtes pas identifié(e).

#1 03/05/2019 14:11:23

GIP Symaris
Membre

Erreur remontée lors de l'exécution d'un script PL / pgSQL

Bonjour à tous.

Je me permet de poster ce message car je rencontre un problème que je parviens pas à résoudre. Un script m'a été fourni par notre département développement pour mettre à jour certaines données de notre progiciel mais ce dernier me remonte une erreur avec rollback.

Voici le script en question :

--SET client_min_messages TO DEBUG;
--Permet la création de RI 
CREATE OR REPLACE FUNCTION findLastActionFromDonnee
(
   idDonnee integer
)
RETURNS INTEGER AS $BODY$
DECLARE
actionDonneePrecedent record;
tmpActionDonnee integer;
BEGIN
SELECT
t1.id_donnee_psp_suivante AS idDonneeSuivante,
t1.id_psp AS idPsp,
t1.id AS idDonnee,
t3.id AS idAction,
t1.date_creation AS dateCreationDonnee,
t1.date_modification AS dateModifDonnee,
t1.supprime AS supprimeDonnee,
t1.motif_suppression AS motifSupprDonnee,
t3.id_agent_createur AS agentCreateurAction,
t3.id_profil_agent_createur AS profilCreateurAction,
t3.id_agent_modification AS agentModificationAction
INTO actionDonneePrecedent
FROM medical_donnee_psp t1 full
JOIN join_medical_action_donnee_psp t2 ON t1.id=t2.id_donnee_psp full
JOIN  medical_action_psp t3 ON t2.id_action_psp=t3.id
WHERE t1.id_donnee_psp_suivante = idDonnee;
--raise debug 'ID PARCOURU : %', actionDonneePrecedent.idDonnee;
--tant qu'on ne trouve pas de record ayant une action on remonte la grappe des 
IF actionDonneePrecedent.idAction IS NOT NULL
THEN
--on retourne le record qui n'a pas d'id d'action null
RETURN actionDonneePrecedent.idAction;
ELSE
--appel recursif
select findLastActionFromDonnee(actionDonneePrecedent.idDonnee) into tmpActionDonnee;
RETURN tmpActionDonnee;
END IF;
END
$BODY$
LANGUAGE plpgsql;

--Permet la création de RI 
CREATE OR REPLACE FUNCTION createResultatInterlie
(
   idAction integer, --identifiant de l'action pour laquelle on cherche les résultats intermédiaire
   idDonnee integer
)
RETURNS BOOLEAN AS $BODY$

DECLARE
dateModifResultatInter varchar;
idRISuivant varchar;
motifSuppression varchar;
idRIAgentModif varchar;
chaineColonneNullable varchar;
resultatInterCourant record;
BEGIN
--on parcourt les résultats intermédiaires liés à l'action
FOR resultatInterCourant IN
(
   SELECT
   t1.id AS idRi,
   t1.date_creation AS dateCreation,
   t1.date_modification AS dateModification,
   t1.commentaire AS commentaire,
   t1.supprime AS supprime,
   t1.motif_suppression AS motifSuppression,
   t1.id_ri_suivant AS idRiSuivant,
   t1.id_agent_createur AS idAgentCreateur,
   t1.id_agent_modification AS idAgentModification,
   t1.id_profil_agent_createur AS idProfilCreateur
   FROM medical_res_intermediaire t1
   INNER JOIN join_action_res_intermediaire t2 ON t1.id=t2.id_res_intermed
   INNER JOIN medical_action_psp t3 ON t3.id=t2.id_action_psp
   WHERE t3.id=idAction
   ORDER BY t1.id DESC
)
LOOP
-- si un element est trouvé on l'ajoute dans la nouvel table des RI
IF resultatInterCourant.idRi IS NOT NULL
THEN
chaineColonneNullable:='';
idRIAgentModif:='';
motifSuppression:='';
idRISuivant:='';
dateModifResultatInter:='';
--check de la date de modification
IF resultatInterCourant.dateModification IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',DATE_MODIFICATION';
dateModifResultatInter:=',to_timestamp('''||resultatInterCourant.dateModification||''', ''YYYY-MM-DD HH24:MI:SS'')';
ELSE
dateModifResultatInter:=NULL;
END IF;

--check du motif de la suppression
IF resultatInterCourant.motifSuppression IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',MOTIF_SUPPRESSION';

motifSuppression:=',$$'||resultatInterCourant.motifSuppression||'$$';
END IF;

--check de l'agent modificateur
IF resultatInterCourant.idAgentModification IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',ID_AGENT_MODIFICATION';
idRIAgentModif:=','||resultatInterCourant.idAgentModification;
END IF;

--check de l'identifiant suivant
IF resultatInterCourant.idRiSuivant IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',ID_RI_SUIVANT';
idRISuivant:=',' || resultatInterCourant.idRiSuivant;
END IF;
--raise debug 'Value RI : %', actionDonnee;
--raise debug 'Value RI : %',CONCAT('INSERT INTO MEDICAL_RES_INTER_ACTION(ID'||chaineColonneNullable||',DATE_CREATION,COMMENTAIRE,SUPPRIME,ID_AGENT_CREATEUR,ID_PROFIL_AGENT_CREATEUR,ID_ACTIONDONNEE_PSP) VALUES(',resultatInterCourant.idRi,dateModifResultatInter,motifSuppression,idRIAgentModif,idRiSuivant,',','to_timestamp(''',resultatInterCourant.dateCreation,''', ''YYYY-MM-DD HH24:MI:SS'')',',$$',resultatInterCourant.commentaire,'$$,',resultatInterCourant.supprime,',',resultatInterCourant.idAgentCreateur,',',resultatInterCourant.idProfilCreateur,',',idDonnee,')');
--execution des insert dans la table destination des résultats intermédiaires
EXECUTE CONCAT('INSERT INTO MEDICAL_RES_INTER_ACTION(ID'||chaineColonneNullable||',DATE_CREATION,COMMENTAIRE,SUPPRIME,ID_AGENT_CREATEUR,ID_PROFIL_AGENT_CREATEUR,ID_ACTIONDONNEE_PSP) VALUES(',resultatInterCourant.idRi,dateModifResultatInter,motifSuppression,idRIAgentModif,idRiSuivant,',','to_timestamp(''',resultatInterCourant.dateCreation,''', ''YYYY-MM-DD HH24:MI:SS'')',',$$',resultatInterCourant.commentaire,'$$,',resultatInterCourant.supprime,',',resultatInterCourant.idAgentCreateur,',',resultatInterCourant.idProfilCreateur,',',idDonnee,')');
END IF;
--Le traitement se finit une fois tout les RI parcouru
END LOOP;
RETURN NULL;
END
$BODY$

LANGUAGE plpgsql;

--Permet la reprise des données et actions dans la nouvelle table
CREATE OR REPLACE FUNCTION reprise_psp
(
)
RETURNS BOOLEAN AS $BODY$

DECLARE
libelleAction varchar --libelle saisie pour une action
(
   4000
)
;
libelleDonnee varchar --libelle saisie pour une donnée
(
   4000
)
;
actionDonnee record; --résultat de la jointure entre les action et donnée via la tablke de jointure
dateModifDonnee varchar;
idDonneeSuivant varchar;
motifSuppression varchar;
idDonneeAgentModif varchar;
chaineColonneNullable varchar;
maxElementActionDonnee integer;
idActionDonneeTmp integer; --id temporaire
maxElementResultatInterAction integer;
actionDonneeTmp record; --actionDonnee temporaire necessaire dans les traitement quand on rencontre une donnée qui n'est pas liée à une action (on recupere l'action et la donnée précédente)
tmpRI record;
BEGIN
--On parcourt toutes les données qui ont une action de rattachée (inner join) et qui ne sont pas historisé
FOR actionDonnee IN
(
   SELECT
   t1.id_donnee_psp_suivante AS idDonneeSuivante,
   t1.id_psp AS idPsp,
   t1.id AS idDonnee,
   t3.id AS idAction,
   t1.date_creation AS dateCreationDonnee,
   t1.date_modification AS dateModifDonnee,
   t1.supprime AS supprimeDonnee,
   t1.motif_suppression AS motifSupprDonnee,
   t3.id_agent_createur AS agentCreateurAction,
   t3.id_profil_agent_createur AS profilCreateurAction,
   t3.id_agent_modification AS agentModificationAction
   FROM medical_donnee_psp t1 full
   JOIN join_medical_action_donnee_psp t2 ON t1.id=t2.id_donnee_psp full
   JOIN  medical_action_psp t3 ON t2.id_action_psp=t3.id
   ORDER BY t1.id DESC --le order by sert à ne pas avoir de problème de clé étrangère non existante
)
LOOP
--check de la date de modification
IF actionDonnee.idAction IS NOT NULL
THEN
--Récupération du libelle de l'action
SELECT
INTO libelleAction  t1.commentaire
FROM medical_action_psp t1
WHERE t1.id = actionDonnee.idAction ;
ELSE
--Récupération de la donnée précedente pour recupéré les données de l'action précédente
select findLastActionFromDonnee(actionDonnee.idDonnee) into idActionDonneeTmp;
   SELECT
   t1.id_donnee_psp_suivante AS idDonneeSuivante,
   t1.id_psp AS idPsp,
   t1.id AS idDonnee,
   t3.id AS idAction,
   t1.date_creation AS dateCreationDonnee,
   t1.date_modification AS dateModifDonnee,
   t1.supprime AS supprimeDonnee,
   t1.motif_suppression AS motifSupprDonnee,
   t3.id_agent_createur AS agentCreateurAction,
   t3.id_profil_agent_createur AS profilCreateurAction,
   t3.id_agent_modification AS agentModificationAction
   into actionDonneeTmp
   FROM medical_donnee_psp t1 full
   JOIN join_medical_action_donnee_psp t2 ON t1.id=t2.id_donnee_psp full
   JOIN  medical_action_psp t3 ON t2.id_action_psp=t3.id
   WHERE t3.id=idActionDonneeTmp;
--on garde les informations de l'agent du record initial
actionDonnee.profilCreateurAction:= actionDonneeTmp.profilCreateurAction;
actionDonnee.agentCreateurAction:= actionDonneeTmp.agentCreateurAction;
actionDonnee.agentModificationAction:= actionDonneeTmp.agentModificationAction;
--raise debug 'Value donnee : %', actionDonnee;
--raise debug 'Value action : %', actionDonneeTmp;
   --Récupération du libelle de l'action
SELECT
INTO libelleAction  t1.commentaire
FROM medical_action_psp t1
WHERE t1.id = actionDonneeTmp.idAction ;
END IF;

--Recupération du libelle de la donnée
SELECT
INTO libelleDonnee  t4.lang_fr
FROM medical_donnee_psp t1
INNER JOIN join_donnee_psp_ref_donnee t2 ON t1.id=t2.id_donnee_psp
INNER JOIN medical_global_ref_donnee t3 ON t2.id_ref_donnee=t3.id
INNER JOIN i18n t4 ON t3.i18n_libelle=t4.id
WHERE t1.id=actionDonnee.idDonnee;

--initialisation des variables
chaineColonneNullable:='';
dateModifDonnee:='';
idDonneeAgentModif:='';
motifSuppression:='';
idDonneeSuivant:='';

--check de la date de modification
IF actionDonnee.dateModifDonnee IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',DATE_MODIFICATION';
dateModifDonnee:=',to_timestamp('''||actionDonnee.dateModifDonnee||''', ''YYYY-MM-DD HH24:MI:SS'')';
ELSE
dateModifDonnee:=NULL;
END IF;

--check de l'agent modificateur
IF actionDonnee.agentModificationAction IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',ID_AGENT_MODIFICATION';
idDonneeAgentModif:=','||actionDonnee.agentModificationAction;
END IF;

--check du motif de la suppression
IF actionDonnee.motifSupprDonnee IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',MOTIF_SUPPRESSION';
motifSuppression:=',$$'||actionDonnee.motifSupprDonnee ||'$$';
END IF;

--check de l'identifiant suivant
IF actionDonnee.idDonneeSuivante IS NOT NULL
THEN
chaineColonneNullable:= chaineColonneNullable ||',ID_ACTIONDONNEE_PSP_SUIVANTE';
idDonneeSuivant:=',' || actionDonnee.idDonneeSuivante;
END IF;
--raise debug 'Value donnee : %', CONCAT('INSERT INTO MEDICAL_ACTION_DONNEE_PSP(ID'||chaineColonneNullable||',DATE_CREATION,ACTION_COMMENTAIRE, DONNEE_COMMENTAIRE,SUPPRIME,ID_AGENT_CREATEUR,ID_PROFIL_AGENT_CREATEUR,ID_PSP) VALUES(',actionDonnee.idDonnee ,dateModifDonnee,idDonneeAgentModif,motifSuppression,idDonneeSuivant,',','to_timestamp(''',actionDonnee.dateCreationDonnee,''', ''YYYY-MM-DD HH24:MI:SS'')',',$$',libelleAction,'$$,$$',libelleDonnee,'$$,',actionDonnee.supprimeDonnee,',',actionDonnee.agentCreateurAction,',',actionDonnee.profilCreateurAction,',',actionDonnee.idPsp,')');
--Traitement d'enregistrement du couple action/donnée dans la nouvelle table
EXECUTE CONCAT('INSERT INTO MEDICAL_ACTION_DONNEE_PSP(ID'||chaineColonneNullable||',DATE_CREATION,ACTION_COMMENTAIRE, DONNEE_COMMENTAIRE,SUPPRIME,ID_AGENT_CREATEUR,ID_PROFIL_AGENT_CREATEUR,ID_PSP) VALUES(',actionDonnee.idDonnee ,dateModifDonnee,idDonneeAgentModif,motifSuppression,idDonneeSuivant,',','to_timestamp(''',actionDonnee.dateCreationDonnee,''', ''YYYY-MM-DD HH24:MI:SS'')',',$$',libelleAction,'$$,$$',libelleDonnee,'$$,',actionDonnee.supprimeDonnee,',',actionDonnee.agentCreateurAction,',',actionDonnee.profilCreateurAction,',',actionDonnee.idPsp,')');
--Traitement du RI
select createResultatInterlie(actionDonnee.idAction,actionDonnee.idDonnee) into tmpRI;
END LOOP;
--on recupere les identifiant max des elements des tables
select max(id) + 1 into maxElementActionDonnee from medical_action_donnee_psp;
select max(id) + 1 into maxElementResultatInterAction from medical_res_inter_action;
--on drop les sequence des tables
EXECUTE 'DROP SEQUENCE seq_medical_action_donnee_psp';
EXECUTE 'DROP SEQUENCE seq_medical_res_inter_action';
--on recrée les sequences avec comme départ le max des identifiants qui existe dans les tables
EXECUTE 'CREATE SEQUENCE seq_medical_action_donnee_psp START WITH ' || maxElementActionDonnee;
EXECUTE 'CREATE SEQUENCE seq_medical_res_inter_action START WITH ' || maxElementResultatInterAction;

RETURN NULL;
END
$BODY$
LANGUAGE plpgsql;

select reprise_psp();

L'erreur remontée est la suivante :

ExampleExceptionFormatter: exception message was: ERREUR: dépassement de limite (en profondeur) de la pile
Indice : Augmenter le paramètre « max_stack_depth » (actuellement 2048 Ko) après vous être assuré que la limite de profondeur de la pile de la plateforme est adéquate.

Nous avons tenté de modifier les paramètres du serveur de plusieurs manières mais sans succès :
- en modifiant la valeur de ulimit -s et en modifiant le fichier de configuration de postgres au niveau de la variable max_stack_depth : sans effet
- en modifiant le fichier postgresql-9.6.service et en y ajoutant la variable LimitSTACK avec une valeur cohérente avec nos précédents paramètres : a eu pour seul effet de repousser le déclenchement de l'erreur

Après chaque modification nous avons redémarré le daemon.
Version de postgres : 9.6.10

Merci d'avance pour votre aide !

Hors ligne

#2 03/05/2019 14:52:26

gleu
Administrateur

Re : Erreur remontée lors de l'exécution d'un script PL / pgSQL

Avec uniquement ces infos, on ne va pas pouvoir dire grand chose en dehors du fait que la récursivité de la fonction est trop importante par rapport à la taille de la pile et qu'on peut se demander si vous n'êtes pas dans une boucle infinie, ce qui fait exploser la pile.


Guillaume.

Hors ligne

Pied de page des forums