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 06/02/2017 11:29:38

Geo-x
Membre

cannot perform INSERT RETURNING

Bonjour @ tous.

J'ai créé une vue avec quelques règles associées pour le cas notamment, d'insertions.

Cette règle semble cependant connaître quelques dysfonctionnements puisque je reçois le message d'erreur suivant : ERROR:  cannot perform INSERT RETURNING on relation "view_table" suivi de HINT:  You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause.

Je ne comprends pas ce que cela veut dire. Ma règle d'insertion ressemble à ceci (en simplifié pour vous éviter le gros pavé) :

CREATE OR REPLACE RULE "_INSERT" AS
ON INSERT TO view_table DO INSTEAD  INSERT INTO table (id, wkb_geometry, nom) 
VALUES (new.id, new.wkb_geometry, new.nom);

Avez-vous une idée qui me premettrait de sortir de ce pétrin ?

Par avance merci.

Geo-x

Hors ligne

#2 06/02/2017 12:23:28

ruizsebastien
Membre

Re : cannot perform INSERT RETURNING

Bonjour,

et comme ça est-ce que ça marche :

CREATE OR REPLACE RULE "_INSERT" AS
ON INSERT TO view_table DO INSTEAD  INSERT INTO table (id, wkb_geometry, nom)
VALUES (new.id, new.wkb_geometry, new.nom) RETURNING table.*;

par contre les insert doivent avoir cette forme :

INSERT INTO view_table (id, wkb_geometry, nom) values (1,'blabla', 'blabla') returning id;

Mais je ne garantie pas que ça marche comme ça...

Hors ligne

#3 06/02/2017 12:27:32

Geo-x
Membre

Re : cannot perform INSERT RETURNING

Et malheureusement non, j'ai tenté mais j'ai ce message qui apparait : ERROR:  RETURNING list's entry 30 has different type from column "stat_view"

Si j’interprète correctement, c'est parce qu'il y a dans ma vue des colonnes qu'il n'y a pas dans la table source. Alors j'ai essayé de faire quelque chose du genre

RETURNING table.nom,table.wkb_geometry,(...)

, mais pas mieux...

Les new.col1 renvoient des valeurs type 'blablabla', habituellement ça fonctionne :-)

Hors ligne

#4 06/02/2017 12:34:09

gleu
Administrateur

Re : cannot perform INSERT RETURNING

Pourquoi passez-vous par une règle ? il est connu qu'elles sont très difficiles à écrire correctement et qu'il est nettement préférable de passer par un trigger, beaucoup plus simple à mettre en place et à maintenir.

Hors ligne

#5 06/02/2017 12:36:56

Geo-x
Membre

Re : cannot perform INSERT RETURNING

Bonjour Gleu.
J'utilise les triggers pour les tables uniquement mais pas pour les vues, je ne sais pas forcément comment les utiliser correctement.

Comment feriez-vous ?

Hors ligne

#6 06/02/2017 21:36:05

gleu
Administrateur

Re : cannot perform INSERT RETURNING

Comment est définie la vue ? et quelle est votre version de PostgreSQL ?

Hors ligne

#7 07/02/2017 09:18:23

Geo-x
Membre

Re : cannot perform INSERT RETURNING

J'ai compris d’où venait le problème.
Comme indiqué ma vue reprend des champs de la table mais crée des colonnes supplémentaires en calculant des indicateurs.
La vue doit pouvoir afficher les colonnes concernant ces indicateurs, du coup il faut l'intégrer dnas le RETURNING même si ce sont des valeurs "fantômes" du genre

CREATE OR REPLACE RULE "_INSERT" AS
ON INSERT TO view_table DO INSTEAD  INSERT INTO table (id, wkb_geometry, nom) 
VALUES (new.id, new.wkb_geometry, new.nom)
RETURNING id, wkb_geometry, nom, 0::numeric AS indicateur1, 0::numeric AS indicateur2;

Il est nécessaire d'utiliser la même technique, si la vue comporte également un ON UPDATE.

Geo-x

Hors ligne

#8 01/08/2017 08:58:46

Girish
Membre

Re : cannot perform INSERT RETURNING

Bonjour,

Je remonte ce sujet, car je ne comprends pas la solution (et je n'arrive pas à la faire fonctionner). J'ai le même type d'erreur (concernant le type dans la clause RETURNING).
Quels sont les champs renseignés dans la clause RETURNING ?
Que sont ces "colonnes supplémentaires" auxquelles vous faites référence ?

Mes vues sont plus complexe que celles présentées puisqu'elles font appel à d'autres tables liées à ma table originelle par des clés étrangères (ou des relations spatiales, je travaille avec PostGIS).

Pourriez-vous élaborer ? Ce qui suit est un exemple de règle (complexe, comme je l'annonçais), et pour information je cherche à travailler avec un logiciel tiers (QGIS), et tout fonctionnait bien sans besoin de clause RETURNING dans une version, mais plus dans la suivante puisqu'il m'est retourné le message d'erreur indiqué dans la question d'origine ("ERROR:  cannot perform INSERT RETURNING on relation "view_table" suivi de HINT:  You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause.") :

CREATE OR REPLACE RULE _insert AS
    ON INSERT TO zapm_01436_00001.cables DO INSTEAD ( INSERT INTO cablage_pays_gex.cable (id_cable, identifiant, geom, dms, commentaire, longueur, id_type_cable, id_nb_fo, id_role_fibre, id_type_fo, xlbl, ylbl, olbl, t_pol, c_pol, c_cad, cad_lbl, com, xlbl1, ylbl1, olbl1, id_etat, id_dossier, id_situation)
  VALUES (nextval('cablage_pays_gex.cable_id_cable_seq'::regclass), new."Id_SIEA", new.geom, new."DMS", new."Comments", round(st_length(new.geom)), ( SELECT type_cable.id_type_cable
           FROM infos_pays_gex.type_cable
          WHERE new."Cable"::text = type_cable.type::text), ( SELECT nb_fo.id_nb_fo
           FROM infos_pays_gex.nb_fo
          WHERE new."Nb_FO" = nb_fo.nb_fo), ( SELECT role_fibre.id_role_fibre
           FROM infos_pays_gex.role_fibre
          WHERE new."Role"::text = role_fibre.role::text), ( SELECT type_fo.id_type_fo
           FROM infos_pays_gex.type_fo
          WHERE new."Fibre"::text = type_fo.designation::text), new.xlbl, new.ylbl, new.olbl, new."Taille", new."Col_text", new."Col_cadre", new."Cadre ?", new."Affiché ?", new.xlbl1, new.ylbl1, new.olbl1, ( SELECT etat.id_etat
           FROM infos_pays_gex.etat
          WHERE new."Etat"::text = etat.etat::text), ( SELECT dossier.id_dossier
           FROM geo_pays_gex.dossier
          WHERE dossier.designation_axians::text = new."Dossier"::text), ( SELECT situation.id_situation
           FROM infos_pays_gex.situation
          WHERE new."Situation"::text = situation.situation::text));

Merci.

Hors ligne

#9 01/08/2017 09:43:32

Geo-x
Membre

Re : cannot perform INSERT RETURNING

Bonjour Girish.

Dans l'exemple évoqué plus haut, il s'agissait d'effectuer une insertion sur quelques champs de la table et non sur l'intégralité.
Afin que cette insertion puisse fonctionner, il fallait pouvoir "déclarer" l'intégralité des champs de la table d'où le RETURNING (...) appelant tous les champs de la table.

Donc pour répondre à votre question : Quels sont les champs renseignés dans la clause RETURNING ? > Tous les champs de votre table (ou de vos tables)

Cela donnera quelque chose du genre :

RETURNING id_cable, identifiant, geom, dms, commentaire, longueur, id_type_cable, id_nb_fo, id_role_fibre, id_type_fo, xlbl, ylbl, olbl, t_pol, c_pol, c_cad, cad_lbl, com, xlbl1, ylbl1, olbl1, id_etat, id_dossier, id_situation;

Pour information, dans mon cas également il s'agissait d'un travail avec QGis :-)

Geo-x

Hors ligne

#10 01/08/2017 10:17:28

Girish
Membre

Re : cannot perform INSERT RETURNING

Bonjour Geo-x,

Et là je me dis que j'aurais pu me douter qu'il pouvait y avoir du spatial d'impliqué étant donné votre Pseudo ^^

Donc, j'ai essayé cela, mais j'ai un message d'erreur :

ERREUR:  l'entrée 3 de la liste RETURNING a un type différent de la colonne « Nb_FO »
DETAIL:  l'entrée de la liste RETURNING a le type character varying alors que la colonne a le type integer.

Je ne mets aussi à jour que certains champs de ma table (d'autres sont renseignés avec des triggers de manière indépendante).

Si besoin, la définition de la vue en question :

CREATE OR REPLACE VIEW zapm_01436_00001.cables AS 
 SELECT cable.id_cable AS "Id_Axians",
    cable.identifiant AS "Id_SIEA",
    nb_fo.nb_fo AS "Nb_FO",
    type_cable.type AS "Cable",
    role_fibre.role AS "Role",
    type_fo.designation AS "Fibre",
    round(st_length(cable.geom)::numeric, 1) AS "Longueur",
    situation.situation AS "Situation",
    dossier.designation_axians AS "Dossier",
    cable.commentaire AS "Comments",
    cable.dms AS "DMS",
    etat.etat AS "Etat",
    type_affichage.type_affichage AS "Affichage",
    cable.user_modif AS "Modifieur",
    cable.date_modif AS "Modifié",
    cable.user_create AS "Créateur",
    cable.date_create AS "Créé",
    cable.cause_abandon AS "C_abandon",
    cable.date_abandon AS "Abandonné",
    cable.user_abandon AS "Abandon",
    cable.xlbl,
    cable.ylbl,
    cable.olbl,
    cable.t_pol AS "Taille",
    cable.c_pol AS "Col_text",
    cable.c_cad AS "Col_cadre",
    cable.cad_lbl AS "Cadre ?",
    cable.com AS "Affiché ?",
    cable.xlbl1,
    cable.ylbl1,
    cable.olbl1,
    cable.geom
   FROM cablage_pays_gex.cable
     LEFT JOIN infos_pays_gex.nb_fo ON nb_fo.id_nb_fo = cable.id_nb_fo
     LEFT JOIN infos_pays_gex.type_cable ON type_cable.id_type_cable = cable.id_type_cable
     LEFT JOIN infos_pays_gex.role_fibre ON role_fibre.id_role_fibre = cable.id_role_fibre
     LEFT JOIN infos_pays_gex.type_fo ON type_fo.id_type_fo = cable.id_type_fo
     LEFT JOIN infos_pays_gex.etat ON cable.id_etat = etat.id_etat
     LEFT JOIN infos_pays_gex.affichage ON affichage.id_affichage = cable.id_cable AND affichage.vue::text = 'cables'::text AND affichage.schema_dossier::text = 'ZAPM_01436_00001'::text
     LEFT JOIN infos_pays_gex.type_affichage ON type_affichage.id_type_affichage = affichage.id_type_affichage
     LEFT JOIN geo_pays_gex.dossier ON dossier.id_dossier = cable.id_dossier
     LEFT JOIN infos_pays_gex.situation ON situation.id_situation = cable.id_situation;

Pourquoi dans votre suggestion n'avez-vous pas ajouté de "champs fantôme" comme vous l'aviez fait dans votre solution ?

Quoiqu'il en soit je ne comprends ni le principe, ni pourquoi entre QGIS 2.14 et 2.18 quelque chose a changé (et qu'est ce qui a changé), sauriez-vous m'éclairer ?

Je vous remercie de votre attention.
Girish

Hors ligne

#11 01/08/2017 10:41:31

Geo-x
Membre

Re : cannot perform INSERT RETURNING

Je vous ai donné dans l'exemple un RETURNING en lien avec le code que vous m'aviez fourni, par conséquent, dans votre table complète, la colonne geom n'est pas en troisième position mais en dernière position.

Si vous appliquez tel quel l'exemple fournit, vous dites dans votre régle ce que votre vue doit afficher dans une 15aine de champs alors qu'il faudrait lui dire pour une 30aine (champs de votre table). Il faut, dans votre clause RETURNING, que vous disiez ce qu'il doit renvoyer au niveau de votre vue pour les lignes nouvellement insérées.

Exemple :
> Table :

champ1, champ2, champ3,champ4,champ5

> Régle d'insertion :

INSERT INTO (champ1,champ3,champ5) VALUES (new.champ1,new.champ3,new.champ5) RETURNING champ1, '' AS champ2, champ3, 0 AS champ4,champ5

Vous dites ainsi à votre vue ce qu'il faut qu'elle affiche pour ces lignes nouvellement insérées.
Concernant QGis, je ne sais pas s'il s'agit de l'évolution de Postgres qui inclut cette contrainte, ou si c'est celle de QGis...

Geo-x

Hors ligne

#12 01/08/2017 11:56:38

Girish
Membre

Re : cannot perform INSERT RETURNING

Je vous remercie pour votre réponse. Malheureusement j'ai beau tout mettre dans l'ordre, et renseigner la totalité des champs de la vue, j'ai quand même l'erreur.

Auriez-vous encore un peu de temps à m'accorder pour m'aider à résoudre la situation ?

J'ai fait un test avec une nouvelles vue, plus simple et une nouvelle règle :

CREATE OR REPLACE VIEW public.test_cables AS 
 SELECT cable.id_cable AS "Id_Axians",
    cable.identifiant AS "Id_SIEA",
    nb_fo.nb_fo AS "Nb_FO",
    type_cable.type AS "Cable",
    cable.geom
   FROM cablage_pays_gex.cable
     LEFT JOIN infos_pays_gex.nb_fo ON nb_fo.id_nb_fo = cable.id_nb_fo
     LEFT JOIN infos_pays_gex.type_cable ON type_cable.id_type_cable = cable.id_type_cable;

CREATE OR REPLACE RULE _insert AS
    ON INSERT TO public.test_cables DO INSTEAD ( INSERT INTO cablage_pays_gex.cable (id_cable, identifiant, id_nb_fo, id_type_cable, geom)
  VALUES (nextval('cablage_pays_gex.cable_id_cable_seq'::regclass), 
          new."Id_SIEA", 
          ( SELECT nb_fo.id_nb_fo
           FROM infos_pays_gex.nb_fo
          WHERE new."Nb_FO" = nb_fo.nb_fo), 
          ( SELECT type_cable.id_type_cable
           FROM infos_pays_gex.type_cable
          WHERE new."Cable"::text = type_cable.type::text), 
          new.geom)
          RETURNING id_cable, identifiant, id_nb_fo, id_type_cable, geom;
);

Je pense avoir suivi la logique, pourtant j'ai comme message d'erreur :

ERREUR: l'entrée 4 de la liste RETURNING a un type différent de la colonne « Cable »
État SQL :42P17
Détail :l'entrée de la liste RETURNING a le type integer alors que la colonne a le type character varying.

J'ai l'impression que quelque part PostgreSQL indique qu'il devrait avoir un VARCHAR puisque le champs de la vue est effectivement un VARCHAR... Mais encore une fois je suis perdu quant à savoir que faire.

Hors ligne

#13 01/08/2017 12:02:28

Geo-x
Membre

Re : cannot perform INSERT RETURNING

D'une manière générale, dans le RETURNING, lorsque vous souhaitez afficher : un champ caractère vide vous pouvez mettre :

'' AS monchamp

s'il s'agit d'un champ nombre vous pouvez mettre :

0 AS monchamp

dans les deux cas vous pouvez mettre :

NULL AS monchamp

Si je regarde votre exemple, votre INSERT ne concerne pas le champ 'type_cable', par conséquent, il faut que vous le déclariez dans le RETURNING au même titre que le champ 'nb_fo' avec :

NULL AS monchamp

ou 

'' AS monchamp

ou 

0 AS monchamp

selon le type de champ.

Dernière modification par Geo-x (01/08/2017 12:04:24)

Hors ligne

#14 01/08/2017 12:06:10

Girish
Membre

Re : cannot perform INSERT RETURNING

Je crois que j'ai trouvé en fait, il faut renseigner de nouveau la définition de la vue dans la clause RETURNING... Le code qui semble fonctionner est le suivant :

CREATE OR REPLACE RULE _insert AS
    ON INSERT TO public.test_cables DO INSTEAD ( 
    INSERT INTO cablage_pays_gex.cable (id_cable, identifiant, id_nb_fo, id_type_cable, geom)
  VALUES (nextval('cablage_pays_gex.cable_id_cable_seq'::regclass), 
          new."Id_SIEA", 
          ( SELECT nb_fo.id_nb_fo
           FROM infos_pays_gex.nb_fo
          WHERE new."Nb_FO" = nb_fo.nb_fo), 
          ( SELECT type_cable.id_type_cable
           FROM infos_pays_gex.type_cable
          WHERE new."Cable"::text = type_cable.type::text), 
          new.geom)
          RETURNING id_cable, identifiant, id_nb_fo, (select type from infos_pays_gex.type_cable where id_type_cable = cable.id_type_cable), geom;
);

Franchement je ne comprends pas pourquoi il est nécessaire de faire ce qui me paraît complètement délirant, d'autant plus que tout fonctionne bien avec la règle UPDATE. J'ai vraiment l'impression que c'est QGIS qui complique les choses en essayant de déterminer quelle sera la vue au fur et à mesure au lieu de simplement interroger le serveur de nouveau...

Bref, merci de votre aide.

Hors ligne

#15 01/08/2017 13:23:10

Geo-x
Membre

Re : cannot perform INSERT RETURNING

Le problème ne semble pas propre à QGis, j'ai déjà pu constater ce problème lorsque j'ai voulu faire une insertion dans une vue regroupant deux tables directement dans Postgres.

Hors ligne

Pied de page des forums