Existuje mnoho způsobů, jak můžete přimět server Postgres ke spuštění předdefinovaného kódu. Níže je úplný seznam s příklady způsobů, jak můžete nechat server Postgres ukládat předdefinovanou logiku, kterou můžete později použít ze své aplikace.
Funkce SQL
Postgres vám umožňuje vytvářet „uživatelem definované funkce“, kde lze tělo funkce zapsat v podporovaném jazyce. „Funkce SQL“ jsou uživatelem definované funkce napsané v běžném SQL, což je nejjednodušší způsob, jak zapouzdřit složité dotazy a sekvence příkazů SQL.
Zde je několik příkladů:
-- update item price and record the change
CREATE FUNCTION update_price(item text, newprice numeric) RETURNS void AS $$
UPDATE items SET price=$2 WHERE name=$1;
INSERT INTO audit (event, new_price, at, item)
VALUES ('price changed', $2, now(), $1);
$$ LANGUAGE SQL;
-- a function from uuid-osp
CREATE FUNCTION uuid_timestamp_bits(uuid) RETURNS varbit AS
$$ SELECT ('x' || substr($1::text, 15, 4) || substr($1::text, 10, 4) ||
substr($1::text, 1, 8) || substr($1::text, 20, 4))::bit(80)
& x'0FFFFFFFFFFFFFFF3FFF' $$
LANGUAGE SQL STRICT IMMUTABLE;
Funkce SQL mohou přijímat a vracet základní typy, složené typy a řádky. Podporují také proměnný počet argumentů, výchozí hodnoty pro argumenty a polymorfní argumenty. Mohou dokonce vrátit více řádků a napodobovat SELECT z tabulky. Není také nutné, aby vůbec nic vraceli.
Tělo funkce však může obsahovat pouze příkazy SQL. To znamená, že neexistují žádné příkazy řízení toku (if, while, …), proměnné a podobně.
K vytvoření funkce se používá příkaz CREATE FUNCTION. Jako obvykle je můžete ZMĚNIT a ZRUŠIT.
Toto je skvělé místo, kde můžete začít dále kopat:https://www.postgresql.org/docs/current/xfunc-sql.html
Funkce C
Zatímco funkce SQL se nejsnáze zapisují a jsou nejméně výkonné, na druhém konci spektra lze funkce psát v jazyce C a mohou v podstatě dělat cokoli. Takové funkce musí být kódovány v C a sestaveny jako sdílená knihovna, kterou lze dynamicky načítat Postgresem.
Musíte Postgresu sdělit, kam má načíst sdílenou knihovnu, název a podpis funkce:
CREATE FUNCTION sum(integer, integer) RETURNS integer
AS 'myfuncs', 'sum'
LANGUAGE C STRICT;
To říká, že sdílená knihovna myfuncs.so
, přítomný v předdefinované vyhledávací cestě, obsahuje vstupní body, které může Postgres volat, přičemž jeden ze vstupních bodů je „součet“, který lze vyvolat jako funkci.
Skutečný kód v C by byl příliš dlouhý na to, aby jej sem mohl zahrnout, ale vše si můžete přečíst v dokumentaci. V kombinaci s rozhraním SPI (Server Programming Interface) je možné provádět téměř jakoukoli operaci, kterou můžete provádět jakýmkoli jiným způsobem.
Například pomocí zde definovaných funkcí C můžete provádět požadavky HTTP:
SELECT status, content_type FROM http_get('https://postgresql.org/');
Je také možné psát takové sdílené knihovny v jiných jazycích, jako je C++ nebo Go, které mohou vytvářet sdílené knihovny s propojením „C“.
Funkce PL/pgSQL
Jiné než SQL a C můžete psát funkce v procedurálních jazycích . Čtyři takové jazyky jsou podporovány jádrem PostgreSQL – pgSQL, Python, Perl a Tcl. Podpora pro jakýkoli procedurální jazyk samotný pochází ze sdílené knihovny C a funguje podobně jako mod_perl nebo mod_python z éry Apache.
pgSQL je kanonický, nejpoužívanější jazyk podobný SQL, ve kterém jsou psány uložené funkce pro PostgreSQL. Je k dispozici ve výchozím nastavení, protože je nainstalován v template1
.
PL/pgSQL je plnohodnotný jazyk s proměnnými, výrazy a řídicími příkazy; a obsahuje funkce, jako jsou kurzory pro práci zejména s daty SQL. Je to rozsáhle zdokumentováno zde.
Zde je příklad:
CREATE FUNCTION repeat(times integer, s text)
RETURNS text
AS $$
DECLARE
result text;
BEGIN
result := '';
FOR i IN 1..times LOOP
result := result || s;
END LOOP;
RETURN result;
END;
$$
LANGUAGE plpgsql
IMMUTABLE;
-- psql> SELECT repeat(10, '*');
-- repeat
-- ------------
-- **********
-- (1 row)
Další základní procedurální jazyky
Ostatní procedurální jazyky – Python, Perl, Tcl – umožňují vývojářům používat jazyk, se kterým jsou již spokojeni. Přestože podpora těchto jazyků existuje ve zdrojovém stromu Postgres, distribuce obvykle binární soubory standardně neinstalují. Například v Debianu možná budete muset udělat:
sudo apt install postgresql-plpython-11
nainstalovat podporu PL/Python pro PostgreSQL 11.
Ať už používáte k zápisu funkce jakýkoli jazyk, volající nevnímá žádné rozdíly v jeho použití.
Python
Rozšíření PL/Python podporuje funkce zápisu v Pythonu 2 a Pythonu 3. Chcete-li jej nainstalovat, postupujte takto:
CREATE EXTENSION plpythonu;
Zde je funkce napsaná v PL/Pythonu:
CREATE FUNCTION pymax (a integer, b integer)
RETURNS integer
AS $$
if a > b:
return a
return b
$$ LANGUAGE plpythonu;
Prostředí Pythonu, ve kterém tělo funkce běží, má modul nazvaný plpy
se do něj automaticky importují. Tento modul obsahuje metody, které vám umožňují připravovat a spouštět dotazy, zpracovávat transakce a pracovat s kurzory.
Více informací lze nalézt v kapitole 46 dokumentu Postgres.
Perl
No, ano, Perl. Procesy vývoje, testování a sestavení Postgres využívají perlextenzivně a je podporován také jako procedurální jazyk. Chcete-li jej začít používat, ujistěte se, že jsou nainstalovány všechny relevantní binární balíčky pro vaše distro (příklad „postgresql-plperl-nn“ pro Debian) a nainstalujte rozšíření „plperl“.
Zde je funkce napsaná v PL/Perl:
CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
my ($x, $y) = @_;
if (not defined $x) {
return undef if not defined $y;
return $y;
}
return $x if not defined $y;
return $x if $x > $y;
return $y;
$$ LANGUAGE plperl;
Kompletní dokumentace zde.
Tcl
Tcl je další PL podporovaná jádrem Postgres. Zde je příklad:
CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS $$
if {[argisnull 1]} {
if {[argisnull 2]} { return_null }
return $2
}
if {[argisnull 2]} { return $1 }
if {$1 > $2} {return $1}
return $2
$$ LANGUAGE pltcl;
Další informace naleznete v dokumentech zde.
Jednojádrové procedurální jazyky
Kromě těchto jazyků existují projekty s otevřeným zdrojovým kódem, které vyvíjejí a udržují podporu pro další jazyky, jako je Java, Lua, R atd.
Seznam je zde:https://www.postgresql.org/docs/current/external-pl.html
Agregační funkce
Agregační funkce fungují nad sadou hodnot a vracejí jeden výsledek. PostgreSQL má spoustu vestavěných agregačních funkcí (viz úplný seznam zde). Chcete-li například získat směrodatnou odchylku populace všech hodnot ve sloupci, může:
SELECT stddev_pop(grade) FROM students;
Můžete definovat své vlastní agregační funkce, které se chovají podobným způsobem. Auserem definovaný agregát je sestaven z několika jednotlivých samostatných funkcí, které pracují na vnitřním stavu (například vnitřním stavem agregátu, který počítá průměr, mohou být proměnné „součet“ a „počet“).
Zde je uživatelsky definovaný agregát, který počítá medián sady hodnot:
-- from https://wiki.postgresql.org/wiki/Aggregate_Median
CREATE OR REPLACE FUNCTION _final_median(NUMERIC[])
RETURNS NUMERIC AS
$$
SELECT AVG(val)
FROM (
SELECT val
FROM unnest($1) val
ORDER BY 1
LIMIT 2 - MOD(array_upper($1, 1), 2)
OFFSET CEIL(array_upper($1, 1) / 2.0) - 1
) sub;
$$
LANGUAGE 'sql' IMMUTABLE;
CREATE AGGREGATE median(NUMERIC) (
SFUNC=array_append,
STYPE=NUMERIC[],
FINALFUNC=_final_median,
INITCOND='{}'
);
který lze vyvolat jako:
SELECT median(grade) FROM students;
Další informace najdete v dokumentech o agregátech a prohlášení CREATE AGGREGATE.
Uživatelem definované typy
Sdílené knihovny napsané v C, které jsme viděli dříve, mohou nejen definovat funkce, ale také datové typy. Tyto uživatelem definované typy lze použít jako datové typy pro sloupce, stejně jako vestavěné typy. Můžete definovat funkce pro práci s hodnotami vašich uživatelsky definovaných typů.
Definování nového typu vyžaduje trochu kódu. Podívejte se na dokumenty, které vás provedou vytvořením nového typu reprezentujícího komplexní čísla. Zdrojový kód Postgres také obsahuje výukový kód.
Operátoři
Operátory usnadňují používání funkcí (například psaní 1 + 2
spíše než sum(1, 2)
) a operátory pro uživatelem definované typy můžete definovat pomocí příkazu CREATE OPERATOR.
Zde je příklad vytvoření +
operátor, který se mapuje na funkcicomplex_add
který přidá dva complex
čísla:
CREATE OPERATOR + (
leftarg = complex,
rightarg = complex,
function = complex_add,
commutator = +
);
Více informací zde a zde.
Třídy operátorů a rodiny operátorů
Třídy operátorů umožňují vašemu datovému typu pracovat s vestavěným B-stromem a dalšími metodami indexování. Pokud například chcete vytvořit index B-Stromu na sloupci typu „komplex“, budete muset Postgresu sdělit, jak porovnat dvě hodnoty tohoto typu, abyste určili, zda je jedna menší, rovna nebo větší než druhá.
U komplexních typů by bylo dobré porovnávat s celými čísly nebo hodnotami s plovoucí desetinnou čárkou, což je místo, kde přicházejí na řadu operátorové rodiny.
Zde si můžete přečíst vše o třídách operátorů a rodinách.
Spouštěče
Spouštěče jsou mocným mechanismem pro vytváření vedlejších účinků pro běžné operace, i když mohou být nebezpečné, pokud jsou nadměrně používány nebo zneužívány. Spouštěče v podstatě propojují události s funkcemi. Funkci, na kterou se odkazuje, lze vyvolat:
- před nebo po vložení/aktualizaci/smazání řádku tabulky
- na zkrácení tabulky
- místo vložení/aktualizace/smazání řádku zobrazení
Funkci lze vyvolat pro každý řádek ovlivněný příkazem nebo jednou perstatement. A je tu ještě více věcí, jako je kaskádování spouštěčů, které jsou všechny vysvětleny zde.
Spouštěcí funkce mohou být zapsány v C nebo v kterékoli z funkcí PL, ale ne v SQL. Zde je příklad vložení řádku do auditu tabulky za každou aktualizaci provedené na cenu položky .
-- first create the function
CREATE FUNCTION log_update() RETURNS TRIGGER AS $$
INSERT INTO audit (event, new_price, at, item)
VALUES ('price changed', NEW.price, now(), OLD.item);
$$
LANGUAGE plpgsql;
-- then create the trigger
CREATE TRIGGER audit_price_changes
AFTER UPDATE ON items
FOR EACH ROW
WHEN (OLD.price IS DISTINCT FROM NEW.price)
EXECUTE FUNCTION log_update();
Přečtěte si vše o spouštěčích zde spolu s dokumentací CREATE TRIGGER.
Spouštěče událostí
Zatímco spouští reagovat na události DML v jedné tabulce, spouštěče událostí může reagovat na události DDL v konkrétní databázi. Události zahrnují vytvoření, změnu, odstranění různých objektů, jako jsou tabulky, indexy, schémata, pohledy, funkce, typy, operátory atd.
Zde je spouštěč události, který zabraňuje vypuštění objektů ze schématu ‘audit’:
-- create function first
CREATE FUNCTION nodrop() RETURNS event_trigger LANGUAGE plpgsql AS $$
BEGIN
IF EXISTS(
SELECT 1
FROM pg_event_trigger_dropped_objects() AS T
WHERE T.schema_name = 'audit')
THEN
RAISE EXCEPTION 'not allowed to drop objects in audit schema';
END IF;
END $$;
-- create event trigger
CREATE EVENT TRIGGER trigger_nodrop
ON sql_drop
EXECUTE FUNCTION nodrop();
Více informací naleznete zde a v dokumentaci CREATE EVENT TRIGGER.
Pravidla
PostgreSQL přichází s funkcí, která vám umožní přepsat dotazy, než se dostanou do plánovače dotazů. Operace je trochu podobná konfiguraci Nginxu nebo Apache k přepsání příchozí adresy URL před jejím zpracováním.
Zde jsou dva příklady, které ovlivňují příkazy INSERT v tabulce a dělají z nich něco jiného:
-- make inserts into "items" table a no-op
CREATE RULE rule1 AS ON INSERT TO items DO INSTEAD NOTHING;
-- make inserts go elsewhere
CREATE RULE rule2 AS ON INSERT TO items DO INSTEAD
INSERT INTO items_pending_review VALUES (NEW.name, NEW.price);
Tato kapitola z dokumentace obsahuje více informací o pravidlech.
Uložené procedury
Počínaje Postgres 11 je možné vytvářet uložené postupy taky. Ve srovnání s uloženými funkcemi existuje pouze jedna věc navíc, kterou procedury umí – kontrola transakcí.
Zde je příklad:
CREATE PROCEDURE check_commit(v integer)
LANGUAGE plpgsql AS $$
BEGIN
IF v % 2 = 0 THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END $$;
-- call it
CALL check_commit(10);
Více informací naleznete zde a zde.
Další exotické věci
Zahraniční obaly dat
Foreign Data Wrapper (FDW) vám umožní mluvit s jinými zdroji dat, jako je další server Postgres, MySQL, Oracle, Cassandra a další. Veškerá logika pro přístup k cizímu serveru je napsána v C, jako sdílená knihovna.
Existuje dokonce sloupcový obchod s názvem cstore_fdw založený na FDW.
Seznam implementací FDW můžete najít na Postgres Wiki a další dokumentaci zde.
Metody přístupu k indexu
PostgreSQL přichází s typy indexů, jako je B-Strom, hash, GIN a další. Je možné napsat svůj vlastní typ indexu podobný tomuto, jako sdílenou knihovnu C. Více podrobností zde.
Metody přístupu k tabulkám
S nadcházející PostgreSQL 12 bude možné vytvořit vlastní strukturu úložiště dat. Implementací zde popsaného rozhraní můžete ukládat data z řad fyzicky na disk způsobem, který si zvolíte.
Zásuvné moduly logické replikace
V PostgreSQL je logická replikace implementována „dekódováním“ obsahu protokolu pro zápis (WAL) do libovolného formátu (jako je text SQL nebo json) a publikována předplatitelům prostřednictvím replikačních slotů. Toto dekódování se provádí pomocípluginu výstupu logického dekódování , který lze implementovat jako sdílenou knihovnu C, jak je popsáno zde. Sdílená knihovna „test_decoding“ je jedním z takových pluginů a můžete si vytvořit svůj vlastní.
Procedural Language Handler
Můžete také přidat podporu pro svůj oblíbený programovací jazyk jako Postgres PL vytvořením handleru – opět jako sdílenou knihovnu C. Začněte vytvářet PL/Go nebo PL/Rust!
Rozšíření
Rozšíření jsou způsob správy balíků společnosti Postgres. Řekněme, že máte funkci C, která dělá něco užitečného, a několik příkazů SQL, které pro její nastavení vyžadují příkazy „CREATE FUNCTION“. Tato jako „rozšíření“, která Postgres může nainstalovat (a odinstalovat) můžete spojit v jednom jediném kroku (zavoláním „CREATE EXTENSION“). Když vydáte novou verzi, můžete do rozšíření také zahrnout kroky upgradu.
Ačkoli se nejedná o programování na straně serveru samo o sobě, rozšíření jsou standardním a velmi efektivním způsobem, jak zabalit a distribuovat váš kód na straně serveru.
Více informací o rozšířeních naleznete zde a zde.