Der er mange måder, hvorpå du kan få Postgres-serveren til at køre foruddefineret kode. Nedenfor er en omfattende liste med eksempler på måder, hvorpå du kan lade Postgres-serveren gemme foruddefineret logik, som du kan bruge senere fra din applikation.
SQL-funktioner
Postgres lader dig oprette "brugerdefinerede funktioner", hvor funktionsteksten kan skrives på et understøttet sprog. "SQL-funktioner" er brugerdefinerede funktioner, der er skrevet i almindelig SQL, hvilket er den enkleste måde at indkapsle komplekse forespørgsler og sekvenser af SQL-sætninger.
Her er et par eksempler:
-- 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-funktioner kan acceptere og returnere basistyper, sammensatte typer og rækker. De understøtter også variable antal argumenter, standardværdier for argumenter og polymorfe argumenter. De kan endda returnere flere rækker, efterligne en SELECT fra en tabel. Det er heller ikke nødvendigt for dem at returnere noget som helst.
Funktionens krop kan dog kun indeholde SQL-sætninger. Dette betyder, at der ikke er nogen flowkontroludsagn (hvis, mens, …), variabler og lignende.
CREATE FUNCTION-kommandoen bruges til at oprette funktionen. Som sædvanlig kan du ÆNDRE og SLIPPE dem.
Dette er et godt sted at begynde at grave yderligere i:https://www.postgresql.org/docs/current/xfunc-sql.html
C-funktioner
Mens SQL-funktioner er de nemmeste at skrive og mindst kraftfulde, i den anden ende af spektret, kan funktioner skrives i C og kan stort set gøre alt. Sådanne funktioner skal kodes i C og bygges som et delt bibliotek, der dynamisk kan indlæses af Postgres.
Du skal fortælle Postgres, hvor det delte bibliotek skal indlæses, navnet og signaturen på funktionen:
CREATE FUNCTION sum(integer, integer) RETURNS integer
AS 'myfuncs', 'sum'
LANGUAGE C STRICT;
Dette siger, at det delte bibliotek myfuncs.so
, til stede i en foruddefineret søgesti, indeholder indgangspunkter, som kan kaldes af Postgres, hvor et af indgangspunkterne er 'sum', som kan påkaldes som en funktion.
Den faktiske kode i C ville være for lang til at inkludere her, men du kan læse alt om den i dokumenterne. Kombineret med serverprogrammeringsgrænsefladen (SPI) er det muligt at udføre næsten enhver operation, som du kan gøre på en hvilken som helst anden måde.
For eksempel, med C-funktionerne defineret her, kan du udføre HTTP-anmodninger:
SELECT status, content_type FROM http_get('https://postgresql.org/');
Det er også muligt at skrive sådanne delte biblioteker på andre sprog som C++ eller Go, som kan bygge delte biblioteker med "C"-links.
PL/pgSQL-funktioner
Ud over SQL og C kan du skrive funktioner på proceduresprog . Fire sådanne sprog understøttes af kerne PostgreSQL – pgSQL, Python, Perl og Tcl. Understøttelse af ethvert proceduresprog i sig selv kommer fra et C-delt bibliotek og fungerer meget som mod_perl eller mod_python fra Apache-æraen.
pgSQL er det kanoniske, mest brugte, SQL-lignende sprog, hvori lagrede funktioner til PostgreSQL er skrevet. Den er tilgængelig som standard, takket være den er installeret i template1
.
PL/pgSQL er et fuldgyldigt sprog med variabler, udtryk og kontroludsagn; og inkluderer funktioner som markører til at arbejde med især SQL-data. Det er omfattende dokumenteret her.
Her er et eksempel:
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)
Andre centrale proceduresprog
De andre proceduresprog - Python, Perl, Tcl - giver udviklere mulighed for at bruge et sprog, de allerede er fortrolige med. Selvom understøttelse af disse sprog findes i Postgres-kildetræet, installerer distributioner normalt ikke binære filer som standard. For eksempel, i Debian skal du måske gøre:
sudo apt install postgresql-plpython-11
for at installere PL/Python-understøttelsen til PostgreSQL 11.
Uanset hvilket sprog du bruger til at skrive en funktion på, opfatter den, der ringer, ingen forskelle i dets brug.
Python
PL/Python-udvidelsen understøtter skrivefunktioner i Python 2 og Python 3. Gør følgende for at installere det:
CREATE EXTENSION plpythonu;
Her er en funktion skrevet i PL/Python:
CREATE FUNCTION pymax (a integer, b integer)
RETURNS integer
AS $$
if a > b:
return a
return b
$$ LANGUAGE plpythonu;
Python-miljøet, som funktionslegemet kører i, har et modul kaldet plpy
automatisk importeret til den. Dette modul indeholder metoder, der lader dig forberede og køre forespørgsler, håndtere transaktioner og arbejde med markører.
Mere information kan findes i kapitel 46 i Postgres docs.
Perl
Nå, ja, Perl. Postgres udviklings-, test- og byggeprocesser bruger Perlextensively, og det understøttes også som et proceduresprog. For at begynde at bruge det, sørg for at alle relevante binære pakker til din distro er installeret (eksempel “postgresql-plperl-nn” for Debian) og installer udvidelsen “plperl”.
Her er en funktion skrevet i 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;
Fuld dokumentation her.
Tcl
Tcl er endnu en PL understøttet af kerne Postgres. Her er et eksempel:
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;
For mere information, se dokumenterne her.
Ikke-core proceduresprog
Ud over disse sprog er der open source-projekter, der udvikler og vedligeholder support til andre som Java, Lua, R osv.
Der er en liste her:https://www.postgresql.org/docs/current/external-pl.html
Aggregerede funktioner
Aggregerede funktioner fungerer over et sæt værdier og returnerer et enkelt resultat.PostgreSQL har en masse indbyggede aggregerede funktioner (se en komplet liste her). For at få populationens standardafvigelse for alle værdierne i en kolonne, skal du f.eks. kan:
SELECT stddev_pop(grade) FROM students;
Du kan definere dine egne samlede funktioner, der opfører sig på samme måde. Auser-defineret aggregat er samlet ud fra nogle få individuelle selvstændige funktioner, der fungerer på den interne tilstand (f.eks. kunne den interne tilstand af et aggregat, der beregner gennemsnittet, være "sum"- og "tælle"-variabler).
Her er et brugerdefineret aggregat, der beregner medianen af et sæt værdier:
-- 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='{}'
);
som kan påberåbes som:
SELECT median(grade) FROM students;
For mere information, se dokumenterne om aggregater og CREATE AGGREGATE-erklæringen.
Brugerdefinerede typer
De delte biblioteker skrevet i C, som vi så tidligere, kan ikke bare definere funktioner, men også datatyper. Disse brugerdefinerede typer kan bruges som datatyper for kolonner ligesom de indbyggede typer. Du kan definere funktioner for at arbejde med værdierne for dine brugerdefinerede typer.
Det kræver lidt kode at definere en ny type. Se dokumenterne her, der leder dig gennem oprettelse af en ny type til at repræsentere komplekse tal. Postgres-kilden indeholder også vejledningskode til dette.
Operatører
Operatører gør funktioner nemmere at bruge (for eksempel at skrive 1 + 2
i stedet for sum(1, 2)
), og du kan definere operatorer for brugerdefinerede typer ved hjælp af CREATE OPERATOR-sætningen.
Her er et eksempel på at oprette en +
operator, der knytter sig til funktionencomplex_add
der tilføjer to complex
tal:
CREATE OPERATOR + (
leftarg = complex,
rightarg = complex,
function = complex_add,
commutator = +
);
Mere information her og her.
Operatorklasser og operatørfamilier
Operatørklasser lader din datatype arbejde med det indbyggede B-Tree og andre indekseringsmetoder. For eksempel, hvis du vil oprette et B-Tree-indeks på en kolonne af typen "kompleks", skal du fortælle Postgres, hvordan man sammenligner to værdier af denne type for at afgøre, om den ene er mindre, lig med eller større end den anden.
Det ville være godt for komplekse typer at blive sammenlignet med heltal eller flydende pointværdier, hvilket er her operatørfamilier kommer ind i billedet.
Du kan læse alt om operatørklasser og familier her.
Triggere
Triggere er en kraftfuld mekanisme til at skabe bivirkninger ved normale operationer, selvom de kan være farlige, hvis de overbruges eller misbruges. I det væsentlige forbinder udløsere hændelser til funktioner. Den funktion, der henvises til, kan aktiveres:
- før eller efter indsættelse/opdatering/sletning af en række i en tabel
- ved afkortning af en tabel
- i stedet for at indsætte/opdatere/slette en række af en visning
Funktionen kan aktiveres for hver række, der er påvirket af et udsagn, eller én gang perstatement. Og der er endnu flere ting, som f.eks. kaskade af triggere, som alle er forklaret her.
Trigger-funktioner kan skrives i C eller i enhver af PL-funktionerne, men ikke i SQL. Her er et eksempel på at indsætte en række i en revision tabel for hver opdatering, der er lavet til prisen af en vare .
-- 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();
Læs alt om triggere her sammen med CREATE TRIGGER-dokumentationen.
Begivenhedsudløsere
Mens trigger reagere på DML-hændelser på en enkelt tabel, hændelsesudløser kan reagere på DDL-hændelser på en bestemt database. Hændelser omfatter oprettelse, ændring, slip af en række objekter, såsom tabeller, indekser, skemaer, visninger, funktioner, typer, operatorer osv.
Her er en hændelsesudløser, der forhindrer tab af objekter fra 'audit'-skemaet:
-- 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();
Mere information kan findes her og i CREATE EVENT TRIGGER-dokumentationen.
Regler
PostgreSQL kommer med en funktion, der lader dig omskrive forespørgsler, før den kommer til forespørgselsplanlæggeren. Handlingen ligner lidt at konfigurere Nginx eller Apache til at omskrive en indkommende URL, før den behandles.
Her er to eksempler, der påvirker INSERT-udsagn på en tabel og gør dem til noget andet:
-- 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);
Dette kapitel fra dokumentationen har mere information om regler.
Lagrede procedurer
Fra Postgres 11 er det muligt at oprette lagrede procedurer også.Sammenlignet med lagrede funktioner er der kun én ekstra ting, procedurer kan gøre – transaktionskontrol.
Her er et eksempel:
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);
Se her og her for mere information.
Andre eksotiske ting
Udenlandske dataindpakninger
Foreign Data Wrappers (FDW'er) lader dig tale med andre datakilder, som en anden Postgres-server, MySQL, Oracle, Cassandra og mere. Al logikken for at få adgang til den fremmede server er skrevet i C, som et delt bibliotek.
Der er endda en søjleformet butik kaldet cstore_fdwbased on FDW.
Du kan finde en liste over FDW-implementering i Postgres Wikian og mere dokumentation her.
Indeksadgangsmetoder
PostgreSQL kommer med indekstyper som B-Tree, hash, GIN og mere. Det er muligt at skrive din egen indekstype, der ligner denne, som et C-delt bibliotek. Flere detaljer her.
Tabeladgangsmetoder
Med den kommende PostgreSQL 12 bliver det muligt at oprette din egen datalagringsstruktur. Ved at implementere grænsefladen, der er beskrevet her, kan du gemme de mange data fysisk på disken på den måde, du vælger.
Logiske replikeringsplugins
I PostgreSQL implementeres logisk replikering ved at "afkode" indholdet af WAL-loggen (Write-ahead) til et vilkårligt format (som SQL-tekst eller json) og udgives til abonnenter via replikeringspladser. Denne afkodning udføres vialogisk afkodningsudgangsplugin , som kan implementeres som et C-delt bibliotek, som beskrevet her. Det delte bibliotek "test_decoding" er et sådant plugin, og du kan bygge dit eget.
Procedural Language Handler
Du kan også tilføje support til dit foretrukne programmeringssprog som en Postgres PL ved at oprette en handler – igen som et C-delt bibliotek. Kom i gang her for at oprette PL/Go eller PL/Rust!
Udvidelser
Udvidelser er Postgres' måde at håndtere pakke på. Lad os sige, at du har en C-funktion, der gør noget nyttigt, og et par SQL-sætninger, der laver de nødvendige "CREATE FUNCTION"-sætninger for at sætte den op. Du kan samle disse som en "udvidelse", som Postgres kan installere (og afinstallere) i et enkelt trin (ved at kalde "CREATE EXTENSION"). Når du udgiver en ny version, kan du også inkludere opgraderingstrin i udvidelsen.
Selvom det ikke i sig selv er programmering på serversiden, er udvidelser den standard og meget effektive måde at pakke og distribuere din serverkode på.
Mere information om udvidelser kan findes her og her.