Složený příkaz

Z PostgreSQL
Skočit na navigaci Skočit na vyhledávání

Složený příkaz je v podstatě základním blokem jazyka SQL/PSM. Umožňuje nám zadat posloupnost příkazů, deklarovat lokální proměnné, podminky (conditionals) a subrutiny obsluhy chyb a varování. Nelze křížit deklarace proměnných, kurzorů, obsluhy chyb. Proměnné lze nastavit výchozí hodnotu. Rozsah složeného příkazu určuje dvojice BEGIN END. Syntakticky správný je i prázdný složený příkaz, který nevykoná žádnou akci. V SQL/PSM lze zachytit a obsloužit, kromě chyb, také varování. Toho se poměrně intenzivně využívá, a díky tomu také, obsluha chyb hraje zde významnější roli než v jiných programovacích jazycích. Nejčastěji se zachytává varování NOT FOUND a v podstatě nejpravděpodobnějším způsobem obsluhy je nastavení signální proměnné. Velice často můžete narazit na následující vzor iterace napříč výsledkem dotazu pomocí kurzoru:

CREATE OR REPLACE FUNCTION report()
RETURNS void AS 
$$
  bl:BEGIN
      DECLARE done boolean DEFAULT false;
      DECLARE a, b integer;
      DECLARE cx CURSOR FOR SELECT f.a, f.b FROM Foo f;
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;

      OPEN cx;
      FETCH cx INTO bl.a, bl.b;

      WHILE NOT done
      DO
        PRINT bl.a, bl.b;
        FETCH cx INTO bl.a, bl.b;
      END WHILE;

      CLOSE cx;
    END bl;
$$ LANGUAGE plpgpsm;

Tato konstrukce je v SQL/PSM velice častá a v PL/pgPSM ji můžete použít. Efektivnějším a čistším řešením je použití příkazu FOR. Každý složený příkaz obsahuje implicitní prázdnou obsluhu pro třídu chyb '01' a '02' (NOT FOUND). Tudíž se neobsloužená signalizovaná varování nezobrazí. Nutnosti použít zachycení varování se také vyhnete použitím proměnné SQLSTATE. Tato proměnná není defaultní. Je ji třeba deklarovat jako char(5). Pokud tato proměnná existuje, tak ji systém automaticky nastavuje podle výsledného statusu naposledy provedeného příkazu. Po dokončení libovolné explicitní obsluhy zachycené chyby nebo varování je tato proměnná resetována na výchozí hodnotu '00000'. Implicitní obsluha varování nemá žádný vliv na tuto proměnnou, stejně tak prázdný složený příkaz, řídící příkazy (FOR, LOOP, WHILE, ... (resp. zachovávají SQLSTATE tak jak je nastaví jednak vyhodnocení podmínek řídících příkazů, a jednak z řídících příkazů volané SQL/PSM příkazy)) a příkaz PRINT (pokud by PRINT ovlivňoval SQLSTATE, nedal by se použít k ladění, aniž by neovlivňoval laděnou aplikaci). Jak obsluha chyb, tak příkaz FOR bude dále detailněji popsána.

-- for statement
CREATE OR REPLACE FUNCTION report()
RETURNS void AS 
$$
  FOR 
      SELECT a, b 
         FROM Foo
  DO
    PRINT a, b;
  END FOR;
$$ LANGUAGE plpgpsm;

-- using sqlstate variable
CREATE OR REPLACE FUNCTION report()
RETURNS void AS 
$$
  bl:BEGIN
      DECLARE SQLSTATE char(5);
      DECLARE a, b integer;
      DECLARE cx CURSOR FOR SELECT f.a, f.b FROM Foo f;

      OPEN cx;
      FETCH cx INTO bl.a, bl.b;

      WHILE SQLSTATE = '00000'
      DO
        PRINT bl.a, bl.b;
        FETCH cx INTO bl.a, bl.b;
      END WHILE;

      CLOSE cx;
    END bl;
$$ LANGUAGE plpgpsm;

Zachycení a ošetření varování není nijak náročné a nemusíte se obávat použít tuto techniku. Něco jiného je zachycení výjimky, které si vynucuje vytváření bodů návratu (save points), což sebou nese určitou režii. Proto by se výjimky měly ošetřovat pouze v těch případech, kdy máme jasno, jak výjimku ošetřit. Výjimky by se neměly používat k zakrytí nedostatků a chyb programu. V tomto ohledu nejméně efektivní je CONTINUE handler pro libovolnou výjimku, kdy se bod návratu vkládá před každý příkaz v bloku.

Syntaxe

[navesti ':'] BEGIN [NOT ATOMIC|ATOMIC]
  [deklarace promennych a podminek]
  [deklarace kurzoru]
  [deklarace chybovych handleru]
  [seznam SQL/PSM prikazu oddelenych strednikem]
END [navesti] ';'

deklarace promennych a podminek = 
((DECLARE varname [, varname [, ...]] datovy typ [DEFAULT vychozi hodnota]
| DECLARE condname CONDITION [FOR SQLSTATE [VALUE] string]) ';') [ ... ]

deklarace kurzoru = 
(DECLARE cursorname [SCROLL|NO SCROLL] CURSOR FOR ( sql prikaz | nazev predzpracovaneho prikazu ';') [ ... ]

deklarace chybovych handleru =
(DECLARE (CONTINUE|EXIT|UNDO) HANDLER HANDLER FOR 
  ((condname|SQLSTATE [VALUE] string|NOT FOUND|SQLWARNING|SQLEXCEPTION) [, ...])
  SQL/PSM prikaz ';') [...]

Použití kurzorů je popsáno v části věnované použití SQL příkazů v SQL/PSM.

Ošetření chyb

Chybové handlery dělíme na tzv. specifické, kdy je známa SQLSTATE hodnota nebo SQLSTATE třída, nebo tzv. obecné. Obecné handlery jsou deklarovány pro NOT FOUND signál, SQLWARNING libovolné varování (třída 01) nebo libovolnou výjimku (všechny třídy vyjma 00, 01 a 02), tj. SQLEXCEPTION. Specifický handler může být deklarován pro libovolný počet disjunktních SQLSTATE hodnot (tříd). Obecný handler můžeme definovat pouze pro jednu konkrétní třídu určenou identifikátory NOT FOUND, SQLWARNING a SQLEXCEPTION. Každý signál určený hodnotou SQLSTATE bude obsloužen jedním nejvíce specifickým handlerem pro danou SQLSTATE hodnotu, což znamená, ze se nejdříve hledá handler pro SQLSTATE hodnotu. V případě neúspěšného hledání se hledá handler s adekvátní SQLSTATE třídou, a pokud se nenajde, použije se obecný handler (pokud existuje). V rozsahu složeného příkazu nelze:

  • definovat více handlerů pro jeden SQLSTATE
  • definovat více handlerů pro jednu SQLSTATE třidu
  • definovat více stejných obecných handlerů

Při deklaraci podmínky nemusíme uvádět SQLSTATE. V tom případě se použije SQLSTATE P1001, který je vyhrazen pro tento účel. Tuto SQLSTATE hodnotu nelze explicitně specifikovat a tudíž je zajištěno, že se tento signal zachytí pouze uvedením názvu podmínky v seznamu chybového handleru.

CREATE OR REPLACE FUNCTION demo() 
RETURNS void AS
$$ 
  BEGIN
    DECLARE my_cond CONDITION;
    DECLARE CONTINUE HANDLER FOR my_cond PRINT 'my_cond handled';
    SIGNAL my_cond;
  END;
$$ LANGUAGE plpgpsm;