Има много начини, по които можете да накарате Postgres сървъра да изпълнява предварително дефиниран код. По-долу е даден изчерпателен списък с примери за начини, по които можете да оставите сървъра на Postgres да съхранява предварително дефинирана логика, която можете да използвате по-късно от приложението си.
SQL функции
Postgres ви позволява да създавате „функции, дефинирани от потребителя“, където тялото на функцията може да бъде написано на поддържан език. „SQL функции“ са дефинирани от потребителя функции, написани в обикновен SQL, което е най-простият начин за капсулиране на сложни заявки и последователности от SQL изрази.
Ето няколко примера:
-- 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;
SQL функциите могат да приемат и връщат основни типове, съставни типове и редове. Те също така поддържат променлив брой аргументи, стойности по подразбиране за аргументи и полиморфни аргументи. Те дори могат да връщат няколко реда, имитиращи SELECT от таблица. Също така не е необходимо да връщат нищо.
Тялото на функцията обаче може да съдържа само SQL изрази. Това означава, че няма изрази за контрол на потока (if, while, …), променливи и други подобни.
Командата CREATE FUNCTION се използва за създаване на функцията. Както обикновено, можете да ги ПРОМЕНЯТЕ и ИЗПУСКАТЕ.
Това е чудесно място да започнете да копаете по-нататък:https://www.postgresql.org/docs/current/xfunc-sql.html
C функции
Докато SQL функциите са най-лесните за писане и най-малко мощни, в другия край на спектъра, функциите могат да бъдат написани на C и могат да правят почти всичко. Такива функции трябва да бъдат кодирани на C и изградени като споделена библиотека, която може да се зарежда динамично от Postgres.
Трябва да кажете на Postgres къде да зареди споделената библиотека, името и подписа на функцията:
CREATE FUNCTION sum(integer, integer) RETURNS integer
AS 'myfuncs', 'sum'
LANGUAGE C STRICT;
Това казва, че споделената библиотека myfuncs.so
, присъстващ в предварително дефиниран път за търсене, съдържа входни точки, които могат да бъдат извикани от Postgres, като една от входните точки е „сума“, която може да бъде извикана като функция.
Действителният код в C би бил твърде дълъг, за да го включите тук, но можете да прочетете всичко за него в документите. В комбинация със сървърния програмен интерфейс (SPI), е възможно да направите почти всяка операция, която можете да направите по друг начин.
Например, с функциите на C, дефинирани тук, можете да изпълнявате HTTP заявки:
SELECT status, content_type FROM http_get('https://postgresql.org/');
Възможно е също да се напишат такива споделени библиотеки на други езици като C++ или Go, които могат да създават споделени библиотеки с връзки „C“.
PL/pgSQL функции
Освен SQL и C, можете да пишете функции на процедурни езици . Четири такива езика се поддържат от основния PostgreSQL – pgSQL, Python, Perl и Tcl. Поддръжката за всеки процедурен език сама по себе си идва от споделена библиотека на C и работи подобно на mod_perl или mod_python от ерата на Apache.
pgSQL е каноничният, най-използваният, подобен на SQL език, на който са написани съхранените функции за PostgreSQL. Той е достъпен по подразбиране, благодарение на инсталирането му в template1
.
PL/pgSQL е пълноправен език с променливи, изрази и контролни оператори; и включва функции като курсори за работа по-специално със SQL данни. Тук е подробно документирано.
Ето един пример:
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)
Други основни процедурни езици
Другите процедурни езици – Python, Perl, Tcl – позволяват на разработчиците да използват език, който вече им е удобен. Въпреки че поддръжката за тези езици се намира в дървото на изходния код на Postgres, дистрибуциите обикновено не инсталират двоичните файлове по подразбиране. Например, в Debian може да се наложи да направите:
sudo apt install postgresql-plpython-11
за да инсталирате поддръжката на PL/Python за PostgreSQL 11.
Независимо от езика, на който напишете функция, обаждащият се не забелязва никакви разлики в нейното използване.
Python
Разширението PL/Python поддържа функции за писане в Python 2 и Python 3. За да го инсталирате, направете:
CREATE EXTENSION plpythonu;
Ето функция, написана на PL/Python:
CREATE FUNCTION pymax (a integer, b integer)
RETURNS integer
AS $$
if a > b:
return a
return b
$$ LANGUAGE plpythonu;
В средата на Python, в която се изпълнява тялото на функцията, има модул, наречен plpy
автоматично импортирани в него. Този модул съдържа методи, които ви позволяват да подготвяте и изпълнявате заявки, да обработвате транзакции и да работите с курсори.
Повече информация можете да намерите в глава 46 от документите на Postgres.
Perl
Е, да, Perl. Процесите на разработка, тестване и изграждане на Postgres използват Perlextensively и също така се поддържа като процедурен език. За да започнете да го използвате, уверете се, че всички подходящи двоични пакети за вашата дистрибуция са инсталирани (пример „postgresql-plperl-nn“ за Debian) и инсталирайте разширението „plperl“.
Ето функция, написана на 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;
Пълна документация тук.
Tcl
Tcl е още един PL, поддържан от основния Postgres. Ето един пример:
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;
За повече информация вижте документите тук.
Неосновни процедурни езици
Отвъд тези езици има проекти с отворен код, които разработват и поддържат поддръжка за други като Java, Lua, R и др.
Тук има списък:https://www.postgresql.org/docs/current/external-pl.html
Агрегирани функции
Агрегатните функции работят върху набор от стойности и връщат единичен резултат. PostgreSQL има куп вградени агрегатни функции (вижте пълния списък тук). Например, за да получите стандартното отклонение на популацията на всички стойности в колона, вие може:
SELECT stddev_pop(grade) FROM students;
Можете да дефинирате свои собствени агрегатни функции, които се държат по подобен начин. Агрегатът, дефиниран от потребителя, се сглобява от няколко отделни самостоятелни функции, които работят върху вътрешното състояние (например вътрешното състояние на агрегат, който изчислява средна стойност, може да бъде променливи „sum“ и „count“).
Ето дефиниран от потребителя агрегат, който изчислява медианата на набор от стойности:
-- 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='{}'
);
който може да бъде извикан като:
SELECT median(grade) FROM students;
За повече информация вижте документите за агрегатите и оператора CREATE AGGREGATE.
Дефинирани от потребителя типове
Споделените библиотеки, написани на C, които видяхме по-рано, могат не само да дефинират функции, но и типове данни. Тези дефинирани от потребителя типове могат да се използват като типове данни за колони, точно както вградените типове. Можете да дефинирате функции, за да работите със стойностите на вашите дефинирани от потребителя типове.
Отнема малко код, за да се дефинира нов тип. Вижте документите тук, които ви превеждат през създаването на нов тип за представяне на комплексни числа. Източникът на Postgres също съдържа учебен код за това.
Оператори
Операторите правят функциите по-лесни за използване (например писане на 1 + 2
). вместо sum(1, 2)
), и можете да дефинирате оператори за дефинирани от потребителя типове с помощта на оператора CREATE OPERATOR.
Ето пример за създаване на +
оператор, който се съпоставя с функциятаcomplex_add
което добавя две complex
числа:
CREATE OPERATOR + (
leftarg = complex,
rightarg = complex,
function = complex_add,
commutator = +
);
Повече информация тук и тук.
Класове на оператори и семейства на оператори
Операторните класове позволяват на вашия тип данни да работи с вграденото B-Tree и други методи за индексиране. Например, ако искате да създадете индекс на B-Tree в колона от тип „комплекс”, ще трябва да кажете на Postgres как да сравни две стойности от този тип, за да определи дали едната е по-малка, равна или по-голяма от другата.
Би било добре сложните типове да се сравняват с цели числа или стойности с плаваща запетая, където идват семействата оператори.
Можете да прочетете всичко за операторските класове и семейства тук.
Задействания
Тригерите са мощен механизъм за създаване на странични ефекти при нормални операции, въпреки че могат да бъдат опасни, ако се използват прекомерно или злоупотребяват. По същество тригерите свързват събития с функции. Посочената функция може да бъде извикана:
- преди или след вмъкване/актуализация/изтриване на ред от таблица
- при съкращаване на таблица
- вместо вмъкване/актуализиране/изтриване на ред от изглед
Функцията може да бъде извикана за всеки ред, засегнат от изявление, или веднъж perstatement. И има още повече неща, като каскада от тригери, всички от които са обяснени тук.
Функциите за задействане могат да бъдат написани на C или във всяка от функциите на PL, но не и в SQL. Ето пример за вмъкване на ред в одит таблица, за всяка актуализация на цената на артикул .
-- 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();
Прочетете всичко за тригерите тук, заедно с документацията CREATE TRIGGER.
Задействания на събития
Докато задейства отговарят на DML събития в една таблица, задействания на събития може да отговаря на DDL събития в конкретна база данни. Събитията включват създаване, промяна, изпускане на различни обекти, като таблици, индекси, схеми, изгледи, функции, типове, оператори и т.н.
Ето тригер за събитие, който предотвратява изпускането на обекти от схемата за одит:
-- 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();
Повече информация можете да намерите тук и в документацията CREATE EVENT TRIGGER.
Правила
PostgreSQL се предлага с функция, която ви позволява да пренаписвате заявки, преди да стигне до планировщика на заявки. Операцията е донякъде подобна на конфигурирането на Nginx или Apache за пренаписване на входящ URL адрес, преди да го обработи.
Ето два примера, които засягат изразите INSERT в таблица и ги карат да правят нещо друго:
-- 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);
Тази глава от документацията съдържа повече информация за правилата.
Съхранени процедури
Започвайки с Postgres 11, е възможно да се създават съхранени процедури също. В сравнение със съхранените функции, има само едно допълнително нещо, което процедурите могат да направят – контрол на транзакциите.
Ето един пример:
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);
Вижте тук и тук за повече информация.
Други екзотични неща
Обвивки на чужди данни
Обвивките на чужди данни (FDW) ви позволяват да говорите с други източници на данни, като друг Postgres сървър, MySQL, Oracle, Cassandra и др. Цялата логика за достъп до чуждия сървър е написана на C, като споделена библиотека.
Има дори колонен магазин, наречен cstore_fdwbased на FDW.
Можете да намерите списък с внедряването на FDW в Postgres Wiki и още документация тук.
Методи за достъп до индекс
PostgreSQL идва с типове индекси като B-Tree, хеш, GIN и други. Възможно е да напишете свой собствен тип индекс, подобен на този, като споделена библиотека C. Повече подробности тук.
Методи за достъп до таблица
С предстоящия PostgreSQL 12 ще бъде възможно да създадете своя собствена структура за съхранение на данни. Чрез внедряване на интерфейса, описан тук, можете да съхранявате кортежните данни физически на диск по избран от вас начин.
Плъгини за логическа репликация
В PostgreSQL логическата репликация се реализира чрез „декодиране“ на съдържанието на дневника за предварителна запис (WAL) в произволен формат (като SQL текст или json) и се публикува на абонатите през слотове за репликация. Това декодиране се извършва чрезизходен плъгин за логическо декодиране , който може да бъде реализиран като споделена библиотека C, както е описано тук. Споделената библиотека „test_decoding“ е един такъв плъгин и можете да създадете своя собствена.
Процедурен езиков манипулатор
Можете също да добавите поддръжка за вашия любим език за програмиране като Postgres PL, като създадете манипулатор – отново като споделена библиотека на C. Започнете тук, за да създадете PL/Go или PL/Rust!
Разширения
Разширенията са начинът на Postgres за управление на пакети. Да речем, че имате функция C, която прави нещо полезно, и няколко SQL оператора, които правят необходими оператори „CREATE FUNCTION“, за да я настроите. Можете да обедините тези „разширения“, които Postgres може да инсталира (и деинсталира) в една единствена стъпка (чрез извикване на „CREATE EXTENSION“). Когато пуснете нова версия, можете също да включите стъпки за надстройка и в разширението.
Въпреки че не са програмиране от страна на сървъра сами по себе си, разширенията са стандартният и много ефективен начин за пакетиране и разпространение на вашия сървърен код.
Повече информация за разширенията можете да намерите тук и тук.