Vous n'êtes pas identifié(e).
Bonjour à tous !
Rapide mot de présentation pour le nouveau que je suis : créateur de sites web médicaux depuis 20 ans, pour le loisir, je découvre postgre en collaborant pour mon projet de logiciel open source médical avec un tiers fournisseur de base de données médicamenteuse.
L'API d'interaction avec la base postgre est un fichier sql de fonctions pgSQL. Expérience nouvelle pour ce qui me concerne.
Mon souci : les fonctions fournies par le 1/3 renvoient toutes un curseur. Comme cela figure partout dans les exemples que je trouve, mon problème est que le nom de ce curseur est aléatoire.
Mon seul désir c'est d'extraire en PHP les résultats, sans autre forme d'interrogation ou d'arrière pensée.
Quand j'ai le nom du curseur, je pense avoir compris qu'il fallait faire ça :
BEGIN; SELECT get_data('param1', 'param2');FETCH ALL IN "nomDuCurseur";COMMIT; et c'est plié.
Mais comme je n'ai pas le nom du curseur, ça semble infiniment plus compliqué ! En tout cas, j'y ai passé la journée d'hier sans aboutir.
L'idée principale, il y a plus de 100 fonctions différentes, serait d'avoir quelque chose de générique pour ne pas réécrire une fonction pour chacune des originales. J'en suis très loin. J'ai essayé beaucoup (beaucoup) de choses. Je pense être passé à côté d'un truc fondamental, parce que j'en suis à imaginer des usines à gaz ... ça semble trop compliqué pour un usage qui est probablement extrêmement banal.
Bref il est temps de m'en remettre à la communauté, et c'est donc ce que je suis venu faire ici.
Merci par avance à ceux qui me donneront un coup de pouce !
BertrandB
Hors ligne
Bonjour,
Sans exemple de ces fonctions c'est dur de vous répondre, mais j'imagine que vous êtes dans le cas décrit par le paragraphe 41.7.3.5 ici https://www.postgresql.org/docs/9.6/sta … rsors.html
Dernière modification par Marc Cousin (28/01/2018 09:26:49)
Marc.
Hors ligne
Bonjour
Merci pour votre réponse !
Voici un exemple caviardé de fonction, elles ont toutes le même genre de structure :
CREATE OR REPLACE FUNCTION GET_DATA (CHARACTER VARYING) RETURNS REFCURSOR
AS
$BODY$
DECLARE
NA ALIAS FOR $1;
CURRET REFCURSOR;
BEGIN
OPEN CURRET FOR
SELECT blablabla from blablabla ;
RETURN CURRET;
END;
$BODY$
LANGUAGE PLPGSQL;
Il me semble effectivement être dans le cas de la page que vous indiquez. Je l'ai lu et relu ces derniers jours. Je n'arrive pas à comprendre comment je peux agir sans modifier la fonction d'origine pour faire en sorte de connaître le nom de sortie du curseur.
Merci pour vos lumières !
Bertrand B.
Hors ligne
Il suffit normalement de faire select * from mafonction…
select * from get_data('toto') par exemple.
Marc.
Hors ligne
Merci. J'ai bien essayé, mais la réponse avec psql est celle-ci :
get_data
--------------------
<unnamed portal 1>
(1 row)
J'ai demande ceci :
select * from get_data('toto');
Bref, je ne pige pas du tout.
Si je fais un select plus classique sur une table, alors j'ai bien une sortie en tableau des datas.
Bref, je suis paumé !
B.
Hors ligne
Bon, après un rapide test (je n'avais pas fait de fonction retournant un curseur depuis un moment ), je pense que le piège dans lequel vous êtes tombé, c'est qu'un curseur ne vit que dans une transaction:
CREATE OR REPLACE FUNCTION GET_DATA (CHARACTER VARYING) RETURNS REFCURSOR
AS
$BODY$
DECLARE
NA ALIAS FOR $1;
CURRET REFCURSOR;
BEGIN
OPEN CURRET FOR
SELECT * from pg_settings ;
RETURN CURRET;
END;
$BODY$
LANGUAGE PLPGSQL;
marc=> select * from get_data('toto');
get_data
--------------------
<unnamed portal 5>
(1 row)
marc=> fetch all in "<unnamed portal 5>";
ERREUR: le curseur « <unnamed portal 5> » n'existe pas
marc=> begin ;
BEGIN
marc=> select * from get_data('toto');
get_data
--------------------
<unnamed portal 6>
(1 row)
marc=> fetch all in "<unnamed portal 6>";
name | setting | unit | category | short_desc
| extra_desc
| context | vartype | source | min_val | max_val | enumvals
| boot_val | reset_val | sourcefile | sourceline | pending_restart
-------------------------------------+-------------------+------+-------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------
-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------+-------------------+---------+----------------------+-------------+--------------+------------------------------------------------------------
--------------------+-------------------+-------------------+------------+------------+-----------------
allow_system_table_mods | off | | Developer Options | Allows modifications of the structure of system tables.
|
| postmaster | bool | default | | |
| off | off | | | f
application_name | psql | | Reporting and Logging / What to Log | Sets the application name to be reported in statistics and logs.
|
| user | string | client | | |
| | psql | | | f
archive_command | (disabled) | | Write-Ahead Log / Archiving | Sets the shell command that will be called to archive a WAL file.
|
| sighup | string | default | | |
| | | | | f
archive_mode | off | | Write-Ahead Log / Archiving | Allows archiving of WAL files using archive_command.
|
| postmaster | enum | default | | | {always,on,off}
Marc.
Hors ligne
Nous sommes parfaitement d'accord !
Relisez mon premier post, vous verrez que j'avais bien saisi la chose.
Le problème est que si le nom du curseur est aléatoire, comment fait-on ?!?
Tout mon problème est là !
Dans un script, si je ne peux pas récupérer le nom du curseur, alors je ne peux pas faire de fetch all.
Je m'imaginais pouvoir traiter ça comme une sous requête,
FETCH ALL IN (select * in get_data('toto'));
mais ça semble impossible.
Bref, je ne comprends vraiment pas ce concept de retour avec un nom aléatoire qu'il serait impossible de récupérer à coup sûr.
B.
Hors ligne
Ok, vu. On va partir de l'hypothèse que vous essayez de faire un script psql… à adapter suivant le langage... (je ne sais pas de quel langage de script vous parlez)
BEGIN;
SELECT quote_ident(get_data('toto')::text) as tempvar
\gset
FETCH ALL FROM :tempvar;
J'imagine qu'il y a des subtilités à expliquer:
* on caste le refcursor en text afin de pouvoir le protéger avec quote_ident (mettre des double quotes autour, et protéger les double quotes qui pourraient se trouver à l'intérieur). C'est juste pour contourner les limitations des scripts psql… c'est plus simple de faire comme ça que de concatener soi même des quotes
* on envoie le résultat dans tempvar
* et on fetch avec le nom ainsi fabriqué...
Marc.
Hors ligne
Un grand merci !
Il me reste à voir comment passer ce genre de requête via PHP et ça devrait me sortir de l'ornière !
B.
Hors ligne
Bon, c'est tout sauf gagné parce que ce genre de requête n'a visiblement pas du tout envie de passer dans un pg_query ... !
B.
Dernière modification par bertrandb (29/01/2018 09:59:56)
Hors ligne
Normal, c'est un script psql... en pg_query, vous allez faire (grosso modo):
pg_query($conn, "SELECT quote_ident(get_data('toto')::text)");
pg_fetch_array/pg_fetch_row/… pour récupérer la chaîne produite
pg_query($conn, "FETCH ALL FROM $mon_resultat_du_fetch")
pg_fetch_cequevousvoulez du résultat
Marc.
Hors ligne
Cette fois, ce sera un GRAND GRAND MERCI !
J'y suis !
Je n'aurais jamais abouti sans vous !
Je file continuer mon dev !
Bonne journée !
B.
Hors ligne
Bon courage
Marc.
Hors ligne
j'y pense, l'idée de vous retourner un curseur, de la part des développeurs, c'est peut-être d'éviter d'allouer des quantités énormes de RAM, si certaines de ces requêtes ramènent beaucoup d'enregistrements… dans ce cas, vous pouvez transformer le code légèrement…
pg_query($conn, "SELECT quote_ident(get_data('toto')::text)");
pg_fetch_array/pg_fetch_row/… pour récupérer la chaîne produite
$pg_num_rows=-1;
while ($pg_num_rows <> 0)
{
$result=pg_query($conn, "FETCH 1000 FROM $mon_resultat_du_fetch")
$pg_num_rows=pg_num_rows($result);
if ($pg_num_rows > 0)
{
pg_fetch_all()
}
}
Ou une variante qui vous convienne mieux… ça permet de récupérer les résultats 1000 par 1000, et donc d'avoir des allocations mémoire plus raisonnables (surtout en PHP où on limite la mémoire par défaut)
Marc.
Hors ligne
Merci pour le tuyau !
En théorie quand même, mes requêtes sont là pour ramener l'ensemble de ce qu'elles demandent. Mais je garde ça en tête !
B.
Hors ligne
Oui, mais une des limitations actuelles du protocole utilisé par postgres, c'est que si on n'utilise pas de curseur, l'ensemble de la requête est ramenée en mémoire (sur le client, pas de problème sur le serveur) avant de pouvoir commencer à être traitée… donc si vous ramenez 2Go de données alors que le memory_limit de php est à 128MB, ça ne va pas bien se passer.
Marc.
Hors ligne
Effectivement, j'ai cette notion. J'ai augmenté déjà le memory_limit php :-)
B.
Hors ligne