Istnieje wiele sposobów, dzięki którym serwer Postgres może uruchomić wstępnie zdefiniowany kod. Poniżej znajduje się wyczerpująca lista wraz z przykładami sposobów, w jakie serwer Postgres może przechowywać wstępnie zdefiniowaną logikę, którą można później wykorzystać w swojej aplikacji.
Funkcje SQL
Postgres pozwala tworzyć „funkcje zdefiniowane przez użytkownika”, w których treść funkcji może być napisana w obsługiwanym języku. „Funkcje SQL” to zdefiniowane przez użytkownika funkcje napisane w zwykłym języku SQL, co jest najprostszym sposobem enkapsulacji złożonych zapytań i sekwencji instrukcji SQL.
Oto kilka przykładów:
-- 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;
Funkcje SQL mogą akceptować i zwracać typy podstawowe, typy złożone i wiersze. Obsługują również zmienną liczbę argumentów, domyślne wartości argumentów i argumenty polimorficzne. Mogą nawet zwrócić wiele wierszy, naśladując SELECT z tabeli. Nie jest również konieczne, aby w ogóle niczego zwracali.
Treść funkcji może jednak zawierać tylko instrukcje SQL. Oznacza to, że nie ma instrukcji kontroli przepływu (jeśli, while, …), zmiennych i tym podobnych.
Polecenie CREATE FUNCTION służy do tworzenia funkcji. Jak zwykle możesz je ZMIENIĆ i UPUŚĆ.
To świetne miejsce, aby zacząć dalej kopać:https://www.postgresql.org/docs/current/xfunc-sql.html
Funkcje C
Podczas gdy funkcje SQL są najłatwiejsze do napisania i najmniej wydajne, na drugim końcu spektrum funkcje mogą być napisane w C i mogą praktycznie wszystko. Takie funkcje muszą być zakodowane w C i zbudowane jako biblioteka współdzielona, którą Postgres może dynamicznie ładować.
Musisz powiedzieć Postgresowi, gdzie ma załadować bibliotekę współdzieloną, nazwę i podpis funkcji:
CREATE FUNCTION sum(integer, integer) RETURNS integer
AS 'myfuncs', 'sum'
LANGUAGE C STRICT;
To mówi, że współdzielona biblioteka myfuncs.so
, obecny we wstępnie zdefiniowanej ścieżce wyszukiwania, zawiera punkty wejścia, które mogą być wywoływane przez Postgres, przy czym jednym z punktów wejścia jest „suma”, która może być wywołana jako funkcja.
Rzeczywisty kod w języku C byłby zbyt długi, aby go tu zamieścić, ale możesz o nim przeczytać w dokumentacji. W połączeniu z interfejsem programowania serwera (SPI) można wykonać prawie każdą operację, którą można wykonać w inny sposób.
Na przykład, ze zdefiniowanymi tutaj funkcjami C, możesz wykonywać żądania HTTP:
SELECT status, content_type FROM http_get('https://postgresql.org/');
Możliwe jest również napisanie takich bibliotek dzielonych w innych językach, takich jak C++ lub Go, które mogą budować biblioteki dzielone z linkami „C”.
Funkcje PL/pgSQL
Oprócz SQL i C możesz pisać funkcje w językach proceduralnych . Cztery takie języki są obsługiwane przez rdzeń PostgreSQL – pgSQL, Python, Perl i Tcl. Wsparcie dla każdego języka proceduralnego pochodzi ze współdzielonej biblioteki C i działa podobnie jak mod_perl lub mod_python z ery Apaczów.
pgSQL jest kanonicznym, najczęściej używanym językiem podobnym do SQL, w którym napisane są przechowywane funkcje dla PostgreSQL. Jest dostępny domyślnie dzięki uprzejmości instalacji w template1
.
PL/pgSQL jest pełnoprawnym językiem ze zmiennymi, wyrażeniami i instrukcjami kontrolnymi; i zawiera funkcje, takie jak kursory do pracy w szczególności z danymi SQL. Jest to obszernie udokumentowane tutaj.
Oto przykład:
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)
Inne podstawowe języki proceduralne
Inne języki proceduralne — Python, Perl, Tcl — pozwalają programistom na używanie języka, z którym są już zaznajomieni. Chociaż obsługa tych języków znajduje się w drzewie źródłowym Postgresa, dystrybucje zwykle domyślnie nie instalują plików binarnych. Na przykład w Debianie być może będziesz musiał zrobić:
sudo apt install postgresql-plpython-11
zainstalować obsługę PL/Pythona dla PostgreSQL 11.
Bez względu na język, w jakim piszesz funkcję, osoba wywołująca nie dostrzega żadnych różnic w jej użyciu.
Python
Rozszerzenie PL/Python obsługuje pisanie funkcji w Python 2 i Python 3. Aby je zainstalować, wykonaj:
CREATE EXTENSION plpythonu;
Oto funkcja napisana w PL/Python:
CREATE FUNCTION pymax (a integer, b integer)
RETURNS integer
AS $$
if a > b:
return a
return b
$$ LANGUAGE plpythonu;
Środowisko Pythona, w którym działa treść funkcji, ma moduł o nazwie plpy
automatycznie importowane do niego. Ten moduł zawiera metody, które pozwalają przygotowywać i uruchamiać zapytania, obsługiwać transakcje i pracować z kursorami.
Więcej informacji można znaleźć w rozdziale 46 dokumentacji Postgres.
Perl
No tak, Perlu. Procesy rozwoju, testowania i budowania Postgresa wykorzystują Perlextensively, a także jest obsługiwany jako język proceduralny. Aby zacząć go używać, upewnij się, że wszystkie odpowiednie pakiety binarne dla twojej dystrybucji zostały zainstalowane (na przykład „postgresql-plperl-nn” dla Debiana) i zainstaluj rozszerzenie „plperl”.
Oto funkcja napisana w 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;
Pełna dokumentacja tutaj.
Tcl
Tcl to kolejny PL wspierany przez Core Postgres. Oto przykład:
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;
Aby uzyskać więcej informacji, zapoznaj się z dokumentacją tutaj.
Niepodstawowe języki proceduralne
Poza tymi językami istnieją projekty open source, które rozwijają i utrzymują wsparcie dla innych, takich jak Java, Lua, R itp.
Lista znajduje się tutaj:https://www.postgresql.org/docs/current/external-pl.html
Funkcje agregujące
Funkcje agregujące działają na zbiorze wartości i zwracają pojedynczy wynik. PostgreSQL ma kilka wbudowanych funkcji agregujących (zobacz pełną listę tutaj). Na przykład, aby uzyskać odchylenie standardowe populacji wszystkich wartości w kolumnie, może:
SELECT stddev_pop(grade) FROM students;
Możesz zdefiniować własne funkcje agregujące, które zachowują się w podobny sposób. Agregat zdefiniowany przez użytkownika składa się z kilku indywidualnych samodzielnych funkcji, które działają w stanie wewnętrznym (na przykład stanem wewnętrznym agregatu obliczającego średnią może być zmienne „suma” i „liczba”).
Oto agregat zdefiniowany przez użytkownika, który oblicza medianę zbioru wartości:
-- 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='{}'
);
które można wywołać jako:
SELECT median(grade) FROM students;
Aby uzyskać więcej informacji, zapoznaj się z dokumentacją dotyczącą agregatów oraz oświadczeniem CREATE AGGREGATE.
Typy zdefiniowane przez użytkownika
Współdzielone biblioteki napisane w C, które widzieliśmy wcześniej, mogą nie tylko definiować funkcje, ale także typy danych. Te typy zdefiniowane przez użytkownika mogą być używane jako typy danych dla kolumn, podobnie jak typy wbudowane. Możesz zdefiniować funkcje do pracy z wartościami typów zdefiniowanych przez użytkownika.
Zdefiniowanie nowego typu zajmuje trochę kodu. Zapoznaj się z dokumentami, które przeprowadzą Cię przez proces tworzenia nowego typu do reprezentowania liczb zespolonych. Źródło ThePostgres zawiera również kod samouczka do tego.
Operatorzy
Operatory ułatwiają korzystanie z funkcji (na przykład pisanie 1 + 2
zamiast sum(1, 2)
) i możesz zdefiniować operatory dla typów zdefiniowanych przez użytkownika za pomocą instrukcji CREATE OPERATOR.
Oto przykład tworzenia +
operator, który mapuje do funkcjicomplex_add
który dodaje dwa complex
numery:
CREATE OPERATOR + (
leftarg = complex,
rightarg = complex,
function = complex_add,
commutator = +
);
Więcej informacji tutaj i tutaj.
Klasy operatorów i rodziny operatorów
Klasy operatorów umożliwiają pracę typu danych z wbudowanymi metodami B-Tree i innymi metodami indeksowania. Na przykład, jeśli chcesz utworzyć indeks B-Tree w kolumnie typu „złożony”, musisz powiedzieć Postgresowi, jak porównać dwie wartości tego typu, aby określić, czy jedna jest mniejsza, równa lub większa od drugiej.
Byłoby dobrze, aby typy złożone były porównywane z liczbami całkowitymi lub wartościami zmiennoprzecinkowymi, do których wkraczają rodziny operatorów.
Możesz przeczytać wszystko o klasach i rodzinach operatorów tutaj.
Wyzwalacze
Wyzwalacze są potężnym mechanizmem tworzenia efektów ubocznych dla normalnych operacji, chociaż mogą być niebezpieczne, jeśli są nadużywane lub nadużywane. Zasadniczo wyzwalacze łączą zdarzenia z funkcjami. Funkcja, do której się odwołuje, może zostać wywołana:
- przed lub po wstawieniu/aktualizacji/usunięciu wiersza tabeli
- przy obcięciu tabeli
- zamiast wstawiania/aktualizowania/usuwania wiersza widoku
Funkcja może być wywoływana dla każdego wiersza, na który ma wpływ instrukcja, lub jednorazowo. Jest jeszcze więcej rzeczy, takich jak kaskada wyzwalaczy, z których wszystkie wyjaśniono tutaj.
Funkcje wyzwalaczy można napisać w języku C lub w dowolnej funkcji PL, ale nie w języku SQL. Oto przykład wstawienia wiersza do audytu tabela, za każdą aktualizację ceny przedmiotu .
-- 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();
Przeczytaj wszystko o wyzwalaczach tutaj, wraz z dokumentacją CREATE TRIGGER.
Wyzwalacze zdarzeń
Podczas gdy wyzwalacze odpowiadać na zdarzenia DML na jednej tabeli, wyzwalacze zdarzeń może odpowiadać na zdarzenia DDL w określonej bazie danych. Zdarzenia obejmują tworzenie, zmianę, upuszczanie różnych obiektów, takich jak tabele, indeksy, schematy, widoki, funkcje, typy, operatory itp.
Oto wyzwalacz zdarzenia, który zapobiega upuszczaniu obiektów ze schematu audytu:
-- 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();
Więcej informacji można znaleźć tutaj oraz w dokumentacji UTWÓRZ WYZWALANIE ZDARZEŃ.
Zasady
PostgreSQL jest wyposażony w funkcję, która pozwala przepisać zapytania, zanim trafi do planera zapytań. Operacja jest nieco podobna do konfigurowania Nginx lub Apache w celu przepisania przychodzącego adresu URL przed jego przetworzeniem.
Oto dwa przykłady, które wpływają na polecenia INSERT w tabeli i sprawiają, że robią coś innego:
-- 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);
Ten rozdział z dokumentacji zawiera więcej informacji o zasadach.
Procedury przechowywane
Począwszy od Postgres 11, możliwe jest tworzenie zapisanych procedur W porównaniu z funkcjami przechowywanymi jest tylko jedna dodatkowa rzecz, którą mogą zrobić procedury – kontrola transakcji.
Oto przykład:
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);
Zobacz tutaj i tutaj, aby uzyskać więcej informacji.
Inne egzotyczne rzeczy
Obce opakowania danych
Zagraniczne opakowania danych (FDW) umożliwiają komunikację z innymi źródłami danych, takimi jak inny serwer Postgres, MySQL, Oracle, Cassandra i inne. Cała logika dostępu do obcego serwera jest napisana w C jako biblioteka współdzielona.
Istnieje nawet sklep kolumnowy o nazwie cstore_fdw oparty na FDW.
Listę implementacji FDW można znaleźć w Postgres Wiki i więcej dokumentacji tutaj.
Metody dostępu do indeksu
PostgreSQL zawiera typy indeksów, takie jak B-Tree, hash, GIN i inne. Możliwe jest napisanie własnego typu indeksu podobnego do tego, jako biblioteki współdzielonej C. Więcej szczegółów tutaj.
Metody dostępu do tabel
Wraz z nadchodzącym PostgreSQL 12, możliwe będzie stworzenie własnej struktury przechowywania danych. Implementując opisany tutaj interfejs, możesz przechowywać dane krotki fizycznie na dysku w wybrany przez siebie sposób.
Wtyczki replikacji logicznej
W PostgreSQL replikacja logiczna jest implementowana przez „dekodowanie” zawartości dziennika zapisu z wyprzedzeniem (WAL) do dowolnego formatu (takiego jak tekst SQL lub json) i publikowana dla subskrybentów przez szczeliny replikacji. To dekodowanie odbywa się za pomocąwtyczki wyjścia dekodowania logicznego , która może być zaimplementowana jako biblioteka współdzielona C, jak opisano tutaj. Biblioteka współdzielona „test_decoding” jest jedną z takich wtyczek i możesz zbudować własną.
Proceduralny program obsługi języka
Możesz także dodać obsługę swojego ulubionego języka programowania jako Postgres PL, tworząc program obsługi – ponownie jako bibliotekę współdzieloną C. Zacznij tutaj, aby tworzyć PL/Go lub PL/Rust!
Rozszerzenia
Rozszerzenia to sposób zarządzania pakietami Postgresa. Załóżmy, że masz funkcję C, która robi coś użytecznego, i kilka instrukcji SQL, które sprawiają, że do jej skonfigurowania niezbędne są instrukcje „CREATE FUNCTION”. Możesz połączyć te „rozszerzenia”, które Postgres może zainstalować (i odinstalować) w jednym kroku (wywołując „UTWÓRZ ROZSZERZENIE”). Kiedy wydajesz nową wersję, możesz również uwzględnić kroki aktualizacji również w rozszerzeniu.
Chociaż nie jest to programowanie po stronie serwera samo w sobie, rozszerzenia są standardowym i bardzo wydajnym sposobem pakowania i dystrybucji kodu po stronie serwera.
Więcej informacji o rozszerzeniach można znaleźć tutaj i tutaj.