Postgresql függvény 2db. INOUT paraméterrel

Sziasztok!

PostgreSQL-ben próbálok egy adatbázist összeállítani, és egy TRIGGER-ből hívott pl/pgsql függvény kifogott rajtam.
A függvény fejléce a következő:

CREATE OR REPLACE FUNCTION next_phs_link(
INOUT rec phs_links, -- Az aktuális fizikai link rekord, majd a következő
INOUT sh portshare, -- SHARE, ha NULL, akkor nincs következő rekord
IN dir linkdirection -- Irány Right: 1-2->, Left <-1-2
) AS $$
...
$$ LANGUAGE plpgsql;

Az első paraméter egy rekord, a második, és harmadik egy-egy saját enumerációs típus.
Meghívva a függvényt, függvény egy új rekord értékkel térne vissza, és egy másik enumerációs értékkel.
Ha simán meghívom a függvényt:
RAISE next_phs_link(lrec_r, psh, dir);

Akkor a parancs után a vártnak megfelelően megváltozik a psh változó értéke, de a lrec_r nem, pedig a függvényben értéket adok neki egy SELECT lekérdezésben.

Találtam a neten egy hívási formát, kipróbáltam:
lrec_r := (next_phs_link(lrec_r, psh, dir)).rec;

Ezzel már megkapta az lrec_r változó a megfelelő értéket, viszont a psh változó értéke maradt a hívás előtti, hiába kapott értéket a függvényben.

Valaki árulja el hogyan kell ezt csinálni, mert én valamit úgy látszik nem értek.
A válaszokat előre is köszönöm.

Hozzászólások

Igen olvastam.
Viszont itt nem egy TRIGGER függvényről van szó, csak megemlítettem, hogy egy TRIGGER függvényből hívom. Elnézést, hiba volt a TRIGGER emlegetése, mivel valószínűleg nincs jelentősége a kérdés szempontjából.

Az egészet nem akartam beidézni, mert elég hosszú. De talán még a megadott háttér információ is csak félreértéseket szült, az a kb. 200sor kód csak arra lenne jó, hogy legyen miben eltévedni.
Egyébként most már nem tudom beidézni (csak nehezen) az eredeti program részletet, mert átírtam úgy, hogy csak egy rekordot adjon vissza a RETURN-al, így megkapom az eredményt.

Ennek ellenére érdekelne, hogyan kell olyan függvényt írni (vagy hogyan kell hívni), ami egynél több paramétert is megváltoztat (INOUT), és ezek a paraméterek nem mind skalárok, tehát legalább az egyik egy rekord pl..

Ha jól értelmezem a tapasztaltakat, az INOUT-ként megadott paraméter nem ugyanazt jelenti, mint az algoritmikus nyelvekben a cím szerinti paraméter átadás, mert a függvény visszatértekor a hívó nem mindig (?) azt az értéket látja, amit a hívott beállított, de a szabályokat nem értem.
Vagy pontosan így kéne itt is működnie, csak éppen ez egy bug.

Jó. Tegyük fel, azon túl, hogy nem tudok magyarul, még értetlen is vagyok, amikor nem értem mit nem értesz a kérdésen.
Szóval Én minden bizonnyal tévesen abból a feltételezésből indultam, hogy a pl/pgsql-ben az INOUT egy cím szerinti paraméter átadást jelent. Mint pl a c++ ban (abban tudok programozni, plpgsql-ben úgy tűnik nem annyira):
int fuggveny(int& a, int& b){ .... }

A kérdésem arra vonatkozott, hogy pl. következő test1 függvény esetén:

CREATE OR REPLACE FUNCTION test1(INOUT rec phs_links, INOUT sh portshare, IN dir linkdirection) AS $$
BEGIN
SELECT * INTO rec FROM phs_links WHERE port_id1 = 9;
sh := 'B';
RAISE INFO 'rec.port_id1 = %, sh = %', rec.port_id1, sh;
END;
$$ LANGUAGE plpgsql;

A test1() -et hogyan kell meghívni akkor, ha szükségem van mind a rec rekord típusú paraméter, mind az sh enumeráció típusú paraméter új a test1-ben kapott értékére.

Ha van egy test2 föggvényem:

CREATE OR REPLACE FUNCTION test2() RETURNS void AS $$
DECLARE
lrec phs_links;
psh portshare;
dir linkdirection := 'Right';
BEGIN
SELECT * INTO lrec FROM phs_links WHERE port_id1 = 15;
psh := 'A';
RAISE INFO 'test1() elött: lrec.port_id1 = %, psh = %', lrec.port_id1, psh;
PERFORM test1(lrec, psh, dir);
RAISE INFO 'test1() után: lrec.port_id1 = %, psh = %', lrec.port_id1, psh;
END;
$$ LANGUAGE plpgsql;

Akkor ezt meghívva a következő kimenetet kapom:

INFO: test1() elött: lrec.port_id1 = 15, psh = A
INFO: rec.port_id1 = 9, sh = B
CONTEXT: SQL statement "SELECT test1( $1 , $2 , $3 )"
PL/pgSQL function "test2" line 9 at PERFORM
INFO: test1() után: lrec.port_id1 = 15, psh = A

Tehát az eredmény nem az amit én reméltem.
A dokumentációból meg nem sikerült kihámozni, hogyan kaphatom meg test1 futásának az eredményét, vagyis a rec, és sh új értékét, amit a test1()-ben kapott.

ui.: Mondjuk az eredeti programban (NAGYON!) úgy tűnt, mintha a sh paraméter esetén megkapnám az új értéket, de lehet, hogy elnéztem valamit.

Semmi bajom a magyaroddal, annyit próbáltam mondani, hogy futtatható kódrészlet informatívabb mint ha elmeséled. De nem is ez a fontos, hiszen lupus megválaszolta a problémádat. Annyit tennék hozzá, azért ilyen fura a visszatérési érték kezelése, mivel a plpgsql function-oknak SQL-ből is kell működnie.


CREATE OR REPLACE FUNCTION f1(x INT, y INT, INOUT i INT, OUT sum INT, OUT product INT)
AS $$
SELECT $3 + 3, $1 + $2, $1 * $2
$$ LANGUAGE SQL;

SELECT * FROM f1(11, 42, 3);

SELECT f1(11, 42, 3);

--------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION f2(x INT, y INT, INOUT i INT, OUT sum INT, OUT product INT)
AS $$
BEGIN
i := $3 + 3;
sum := $1 + $2;
product := $1 * $2;
END
$$ LANGUAGE plpgsql;

SELECT * FROM f2(11, 42, 3);

SELECT f2(11, 42, 3);
--------------------------------------------------------------------------
DROP FUNCTION IF EXISTS f3(x INT, y INT, i INT);
DROP TYPE IF EXISTS rtype;

CREATE TYPE rtype AS (i INT, sum INT, product INT);
CREATE OR REPLACE FUNCTION f3(x INT, y INT, i INT) RETURNS rtype
AS $$
DECLARE
o rtype;
BEGIN
o.i := $3 + 3;
o.sum := $1 + $2;
o.product := $1 * $2;

return o;
END
$$ LANGUAGE plpgsql;

SELECT * FROM f3(11, 42, 3);

SELECT f3(11, 42, 3);

Bizonyára elkerülte a figyelmedet a dokumentáció vonatkozó része:

When there are OUT or INOUT parameters, the RETURNS clause can be omitted. If present, it must agree with the result type implied by the output parameters: RECORD if there are multiple output parameters, or the same type as the single output parameter.

Az OUT/INOUT esetén nem a paraméter értéke változik, hanem a függvény az OUT paraméter(eke)t adja vissza.

A test2()-t átalakítva, látható lesz az eredmény:


CREATE OR REPLACE FUNCTION test2() RETURNS void AS $$
DECLARE
  lrec phs_links;
  psh portshare;
  dir linkdirection := 'Right';
  result record;
BEGIN
  SELECT * INTO lrec FROM phs_links WHERE port_id1 = 15;
  psh := 'A';
  RAISE INFO 'test1() elött: lrec.port_id1 = %, psh = %', lrec.port_id1, psh;
  result := test1(lrec, psh, dir);
  RAISE INFO 'test1() után: lrec.port_id1 = %, psh = %', lrec.port_id1, psh;
  RAISE INFO 'result: %', result;
END;
$$ LANGUAGE plpgsql;

Köszönöm szépen a választ.
A manuál idézett része igen, először elkerülte a figyelmemet. Aztán mikor a dolog nem működött, és tettem egy téves megfigyelést is, nagyon nem értettem a dolgot.
A manuálban nem igazán találkoztam rekord a rekordban adattal. Próbáltam is csinálni egy hasonló típust, de nem sikerült.
A manuál vagy nem ad példákat, vagy azok nagyon primitívek. A fellelhető egyéb leírások pedig igencsak a manuálra hajaznak. És lehet, hogy bennem van a hiba, de ha keresek valamit a manuálban, azt nem egykönnyen találom meg.

A row adattípus környékén keresd (pl: 4.2.12).

Rekord a recordban:


create type test_rec as (
  rec phs_links,
  sh portshare
);

CREATE OR REPLACE FUNCTION test2() RETURNS void AS $$
DECLARE
  lrec phs_links;
  psh portshare;
  dir linkdirection := 'Right';
  result test_rec;
BEGIN
  SELECT * INTO lrec FROM phs_links WHERE port_id1 = 15;
  psh := 'A';
  RAISE INFO 'test1() elött: lrec.port_id1 = %, psh = %', lrec.port_id1, psh;
  result := test1(lrec, psh, dir);
  RAISE INFO 'test1() után: lrec.port_id1 = %, psh = %', lrec.port_id1, psh;
  RAISE INFO 'result: lrec.port_id1 = %, psh = %', result.rec.port_id1, result.sh;
END;