04 PL/Python
Obsah článku:
PL/Python
Ďalším z procedurálnych jazykov je PL/Python. Je to klasický objektový Python, ktorý je možné použiť k vytváraniu uložených procedúr v PostgreSQL. PL/Python je však trochu iný ako PL/pgSQL. Ide o procedurálny jazyk s nálepkou Untrasted. Jazyk plpythonu je teda označený ako nedôveryhodný, takže ho bežný užívateľ nemôže používať. Buď musí mať super užívateľské práva, alebo sa zmení dôveryhodnosť jazyka na TRUE. Do tejto množiny Untrasted jazykov patrí okrem PL/Python aj PL/Pearl, či PL/TCL a mnoho iných. Programovanie v týchto jazykoch sa riadi pravidlami a zákonitosťami daného jazyka. Z pohľadu začlenenia vytvorených funkcií do DBS PgSQL je pri spustení týchto funkcií volaný vnútorný interpreter, z tohto pohľadu teda pomalšie riešenie než u PL/pgSQL. Na druhej strane je však možné riešiť mnoho úloh v týchto jazykoch rýchlejšie než priamo v DBS alebo v klientskej aplikácií. Argumentom pre rozšírenie funkčnosti o funkcie napísané v týchto jazykoch je možnosť I/O operácií. Tieto jazyky majú oproti Trusted variantám, väčšie možnosti využitia a väčšie právomoci. Dokážu k príkladu vytvárať nové súbory a pristupovať k ním, či napr. otvárať sockety. To samozrejme so sebou prináša aj zvýšenú opatrnosť pri ich vytváraní. Ako nedôveryhodný je PL/Pythonu označený preto, pretože s ním môže nekalý užívateľ narušiť beh serveru (napríklad jeho zaťažením). Možnosti takéhoto jazyka sú ozaj rozsiahle a je v ňom možné pravdepodobne všetko čo v bežnom jazyku Python. Obtiažnejšie je však ladenie takýchto funkcií.1
PL/Python dokonca zvláda aj komunikáciu s okolitým svetom internetu:
Funkcia vyššie dokáže zistiť IP adresu zvoleného servera, otestujeme ju príkazom:
Pričom sa ukázala sila tohto jazyka. Podobná funkcia by v jazyku C, obsahovala desiatky riadkov kódu.
Od verzie 9.3 sa pomerne zásadných úprav dočkala podpora uložených procedúr v PL/Python. Tento jazyk je rozšírený zväčša medzi užívateľmi PostGISu (Podpora pre geografické objekty). Zásadným krokom vpred je podpora Pythonu 3. Dvojková rada už podporuje Unicode. Parametre funkcie typu pole sú teraz mapované priamo na pole Pythonu.
Pokiaľ sú všetci užívatelia databázového servera dôveryhodní, je možné zmeniť plpythonu na dôveryhodný:
Inou možnosťou by bolo nastaviť konkrétnemu užívateľovi super užívateľské práva:
Inštalácia PL/Python
Často treba nainštalovať samotný PL jazyk na server. Čo v linuxovom systéme OpenSuse, vykonáme príkazom:
Pre použitie jazyka PL/Python, je potrebné si ho najskôr pre danú databázu „aktivovať“ príkazom CREATE EXTENSION. (Je potrebné na to mať taktiež príslušné práva).
Jazyk však môžeme aktivovať aj príkazom CREATE LANGUAGE.
Úspešnosť aktivácie jazyka je najlepšie otestovať funkciou pre zistenie používanej verzie Pythonu:
Výpis verzie používaného Python interpretra:
Základná funkcia v PL/Python
Rovnako ako v PL/pgSQL aj v PL/Python využívame príkaz CREATE LANGUAGE, pre tvorbu funkcií. Syntax takejto funkcie sa nijako nelíši, samozrejme až na zvolený jazyk funkcie a telo funkcie, v ktorom už nevyužívame syntax SQL, ale syntax programovacieho jazyka Python. Mierne odlišné sú aj údajové typy premenných. Riadime sa syntaxou:
Do tela takejto funkcie je dokonca možné importovať Python moduly - externé knižnice, ktoré však musia byť nainštalované v DB serveri (teoreticky je tu možnosť používať virtualenv). PL/Python ponúka modul plpy, ktorý je vždy automaticky importovaný. Obsahuje užitočné metódy ako je notice a info, ktorá zobrazí INFO hlásenie (klasické pythonovské metódy pre výpis, ako je print, nemožno použiť).
Modul plpy a ukážka vo funkcii:
Použitie funkcie hello_wrold():
Konverzie dátových typov
Prvou a poslednou činnosťou, ktorá sa vykoná keď je PL funkcia zavolaná, je konverzia hodnôt argumentov medzi PL/python a PostgreSQL. PostgreSQL typy musia byť konvertované na typy, ktoré podporuje PL/python a návratová hodnota musí byť naopak konvertovaná na typ pre PostgreSQL. Niečo také v PL/pgSQL nie je potrebné nakoľko používa rovnaké typy ako PostgreSQL. PL/Python však používa svoje vlastné typy premenných (Integer, string, date…), a tak je potrebné ich konvertovať.
PostgreSQL | Python 2 | Python 3 | Komentár |
---|---|---|---|
int2, int4 | int | int | |
Int8 | long | int | |
real, double, numeric | float | float | V tomto prípade môže dôjsť k strate presnosti |
bytea | str | bytes | Žiaden prevod kódovania |
text, char(), varchar() | str | Str | Vo verzií Python 2 má reťazec kódovanie servera Vo verzií Python 3 je každý reťazec v kódovaní UNICODE. |
Všetky ostatné typy | str | str |
Je potrebné aby kódovanie reťazcov bolo rovnaké ako kódovanie v PostgreSQL. Reťazce, ktoré nie sú validné vedú k chybe. Nie všetky chyby kódovania dokáže PostgreSQL zachytiť, a tak sa takéto „chybné“ dáta môžu dostať do výsledkov. Reťazce kódované v UNICODE su konvertované na správne kódovanie automaticky. V Pythone 3 su všetky reťazce UNICODE. Inak povedané, všetko pri 0, False, prázdnom reťazci alebo slovníku sa v PostgreSQL stane false. Jediná výnimka je pri None, kde sa kontrola vykoná pred ostatnými konverziami. None sa vždy prekonvertujte na NULL a nie na hodnotu typu boolean.
Prístup k databáze s PL/Python
Vďaka modulu plpy a funkcií plpy.execute() je v Pythone jednoduché získať prístup k databáze. Táto funkcia má na svojom vstupe textový dotaz a vracia zoznam slovníkov (list of dictionaries) ako výsledok. Syntax funkcie plpy.execute():
V nasledujúcom príklade bude premenná tst_id slúžiť ako argument vo vnútri funkcie dotaz_select(). Tento argument bude ďalej predaný podfunkcií plpy.execute().
Spustenie funkcie v psql:
PL/Python funkcie s návratovou hodnotou RECORD
Pre hodnotu návratového typu záznam je možné v PL/Python použiť:
- Sekvenciu alebo zoznam hodnôt v rovnakom poradí ako pri poliach v návratovom zázname.
- Slovník (dictionary) s kľúčmi totožnými ako pri návratovom zázname.
- Triedu alebo typovú inštanciu s atribútmi zhodujúcimi sa s poliami v návratovom zázname.
Nasledujúce príklady funkcií ukazujú všetky tri možné spôsoby:
Prvý za použitia inštancie:
Jednoduchší spôsob za použitia slovníka:
A posledný za použitia n-tice (tuple):
Nula v hranatých zátvorkách na konci príkazu u = plpy.execute(…)[0] vo všetkých troch ukážkach, značí výber prvého riadku výsledku, nakoľko pl.execute aj pre jednoriadkové výsledky vráti zoznam výsledkov. V Pythone je nutné dávať pozor pri kopírovaní kódu. Jednotlivé bloky kódu by mali byť odsadené štyrmi medzerami (PEP-8). Je nutné skontrolovať správnosť odsadenia štandardne vždy, pokiaľ pri vytváraní funkcie dôjde k chybe menom „IndentationError“. Zvyčajne nemá zmysel deklarovať triedu vo vnútri funkcie iba kvôli návratovej hodnote záznam. V prvej funkcií sa nachádza hlavne pre ukážku možností jazyka Python.1
Všetky tieto definované funkcie vracajú rovnaký výsledok:
Zachytenie a spracovanie chyby
Funkcie, ktoré pristupujú do databázy môžu naraziť na chybu, ktorá môže viesť k prerušeniu a vyvolaniu výnimky. Plpy.execute dokáže vyvolať inštanciu podtriedy plpy.SPIError, ktorá štandardne ukončí chod funkcie. Túto chybu je možné zachytiť rovnako ako všetky ostatné výnimky v Pythone za použitia konštrukcie try/except.
Test funkcie:
Modul plpy ponúka tieto funkcie:
- plpy.debug( sprava, **kwargs )
- plpy.log( sprava, **kwargs )
- plpy.info( sprava, **kwargs )
- plpy.notice( sprava, **kwargs )
- plpy.warning( sprava, **kwargs )
- plpy.error( sprava, **kwargs )
- plpy.fatal( sprava, **kwargs )
Funkcie plpy.error a plpy.fatal slúžia na vyvolanie výnimky v Pythone. Obe funkcie sú takmer totožné a slúžia na upozornenie či informovanie užívateľa databázy. Funkcií zadáme správu ako jej argument. Týchto argumentov môže mať funkcia niekoľko a takýto argument môže obsahovať špeciálne kľúčové slová. Tieto slová iba obohacujú chybovú správu. Je možné použiť následujúce kľúčové slová:
- detail
- hint
- sqlstate
- schema_name
- table_name
- column_name
- datatype_name
- constraint_name
Funkcia plpy.error a jej použite vo funkcií:
|
|
Trigger funkcie V PL/Python
Ako aj ďalšie PL jazyky, tak aj PL/Python je možné použiť pre zápis trigger funkcií. Deklarácia trigger funkcie je odlišná od bežnej funkcie iba návratovým typom RETURNS TRIGGER. Jednoduchá trigger funkcia, ktorá informuje volajúceho o spustení triggera, môže vyzerať takto:
Po vytvorení funkcie, otestujeme trigger na tabuľke tst_tabulka:
Samozrejme, predchádzajúca trigger funkcia je trochu zbytočná, ako akýkoľvek trigger, ktorý nevie kedy, a na ktorých dátach bol spustený. Tomuto predchádzajú špeciálne premenné typu slovník (dictionary) nazývané TD. V ktorých sa môžu nachádzať následujúce premenné:TD["event"]
Meno akcie, ktorá trigger spustila, premenná obsahuje jeden z následujúcich reťazcov: INSERT, UPDATE, DELETE alebo TRUNCATE.TD["when"]
Čas kedy sa trigger spustil (AFTER/BEFORE/INSTEAD OF)TD["level"]
ROW alebo STATEMENTTD["old"]
Obsahuje starý riadok (pred modifikáciou / zmazaním).TD["new"]
Obsahuje riadok s novou verziou hodnôt (iba pre INSERT a UPDATE pri akcií DELETE je None).TD["name"]
Meno triggera z príkazu CREATE TRIGGER.TD["table_name"]
Meno tabuľky, nad ktorou sa trigger spúšťa.TD["relid"]
Identifikátor objektu OID tabuľky, nad ktorou sa trigger spustil.TD["args"]
Pokiaľ príkaz CREATE TRIGGER obsahuje argumenty, sú dostupne cez túto premennú. Každý argument má svoj index v slovníku od TD[“args”][0] po TD[“args”][n-1].
Pokiaľ je premenná TD[“when”] (“BEFORE”, “INSTEAD OF”) a TD[“level”] == „ROW“, je možné vrátiť SKIP pre prerušenie akcie. Návratová hodnota None alebo OK ukazuje, že riadok je nemodifikovaný a je v poriadku pokračovať. Hodnota None je v Pythone nastavená implicitne. V prípade že je potrebné modifikovať hodnoty v TD[“new”] a chceme aby PostgreSQL pokračoval s týmito modifikovanými hodnotami, môžeme použiť návratovú premennú MODIFY, ktorá jazyku PL/python naznačí, že dáta sú pozmenené. Čo je možne iba v prípade, ak hodnota premennej TD[“event”] je INSERT alebo UPDATE, inak bude návratová hodnota ignorovaná.
Špeciálne premenné vo funkcií
Nasledujúca trigger funkcia je užitočná pri vytváraný triggerov, vďaka nej je možné jednoducho a prehľadne vypísať všetky tieto premenné. Čo uvítame hlavne vo fáze ladenia funkcií.
Trigger nastavíme aby spustil funkciu po DML operácií nad tabuľkou tst_tabulka :
Trigger sa aktivuje napríklad akciou INSERT:
Za povšimnutie stojí fakt, že hodnoty premennej TD[“args”], ktorá obsahuje argumenty predané funkcií v príkaze CREATE TRIGGER, sú všetky prevedené na typ reťazec a to dokonca aj NULL. Vždy pri programovaní vlastných zložitejších trigger funkcií pridáme aj túto ladiacu (debug) funkciu.