sql >> Databasteknik >  >> RDS >> PostgreSQL

Översikt över programmering på serversidan i PostgreSQL

Det finns många sätt på vilka du kan få Postgres-servern att köra fördefinierad kod. Nedan finns en omfattande lista med exempel på sätt på vilka du kan låta Postgres-servern lagra fördefinierad logik, som du kan använda senare från din applikation.

SQL-funktioner

Postgres låter dig skapa "användardefinierade funktioner", där funktionskroppen kan skrivas på ett språk som stöds. "SQL-funktioner" är användardefinierade funktioner som är skrivna i vanlig SQL, vilket är det enklaste sättet att kapsla in komplexa frågor och sekvenser av SQL-satser.

Här är ett par exempel:

-- 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 acceptera och returnera bastyper, sammansatta typer och rader. De stöder även variabelt antal argument, standardvärden för argument och polymorfa argument. De kan till och med returnera flera rader, efterlikna en SELECTfrån en tabell. Det är inte heller nödvändigt för dem att lämna tillbaka något alls.

Funktionskroppen kan dock bara innehålla SQL-satser. Detta betyder att det inte finns några flödeskontrollsatser (if, while, …), variabler och liknande.

Kommandot CREATE FUNCTION används för att skapa funktionen. Som vanligt kan du ALTERA och SLAPPA dem.

Det här är ett bra ställe att börja gräva vidare i:https://www.postgresql.org/docs/current/xfunc-sql.html

C-funktioner

Medan SQL-funktioner är de enklaste att skriva och minst kraftfulla, i andra änden av spektrumet, kan funktioner skrivas i C och kan i stort sett göra vad som helst. Sådana funktioner måste kodas i C och byggas som ett delat bibliotek som kan laddas dynamiskt av Postgres.

Du måste berätta för Postgres var det delade biblioteket ska laddas, namnet och signaturen för funktionen:

CREATE FUNCTION sum(integer, integer) RETURNS integer
    AS 'myfuncs', 'sum'
    LANGUAGE C STRICT;

Detta säger att det delade biblioteket myfuncs.so , som finns i en fördefinierad sökväg, innehåller ingångspunkter som kan anropas av Postgres, där en av ingångspunkterna är "summa" som kan anropas som en funktion.

Den faktiska koden i C skulle vara för lång för att inkludera här, men du kan läsa allt om den i dokumenten. I kombination med Server Programming Interface (SPI) är det möjligt att göra nästan vilken operation som helst som du kan göra på något annat sätt.

Till exempel, med C-funktionerna definierade här, kan du utföra HTTP-förfrågningar:

SELECT status, content_type FROM http_get('https://postgresql.org/');

Det är också möjligt att skriva sådana delade bibliotek på andra språk som C++ eller Go, vilket kan bygga delade bibliotek med "C"-länkar.

PL/pgSQL-funktioner

Förutom SQL och C kan du skriva funktioner på procedurspråk . Fyra sådana språk stöds av kärnan i PostgreSQL – pgSQL, Python, Perl och Tcl. Stöd för alla procedurspråk i sig kommer från ett delat C-bibliotek och fungerar ungefär som mod_perl eller mod_python från Apache-eran.

pgSQL är det kanoniska, mest använda, SQL-liknande språket där lagrade funktioner för PostgreSQL skrivs. Den är tillgänglig som standard, tack vare att den är installerad i template1 .

PL/pgSQL är ett fullfjädrat språk med variabler, uttryck och kontrollsatser; och innehåller funktioner som markörer för att arbeta med SQL-data i synnerhet. Det är utförligt dokumenterat här.

Här är ett exempel:

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)

Andra grundläggande procedurspråk

De andra procedurspråken – Python, Perl, Tcl – tillåter utvecklare att använda ett språk de redan är bekväma med. Även om stöd för dessa språk finns i Postgres källträd, installerar distributioner vanligtvis inte binärfilerna som standard. Till exempel, i Debian kan du behöva göra:

sudo apt install postgresql-plpython-11

för att installera PL/Python-stödet för PostgreSQL 11.

Oavsett vilket språk du använder för att skriva en funktion på så uppfattar den som ringer inga skillnader i dess användning.

Python

Tillägget PL/Python stöder skrivfunktioner i Python 2 och Python 3. För att installera det, gör:

CREATE EXTENSION plpythonu;

Här är en funktion skriven i PL/Python:

CREATE FUNCTION pymax (a integer, b integer)
  RETURNS integer
AS $$
  if a > b:
    return a
  return b
$$ LANGUAGE plpythonu;

Python-miljön som funktionskroppen körs i har en modul som heter plpy importeras automatiskt till den. Den här modulen innehåller metoder som låter dig förbereda och köra frågor, hantera transaktioner och arbeta med markörer.

Mer information finns i kapitel 46 i Postgres docs.

Perl

Ja, Perl. Postgres utvecklings-, test- och byggprocesser använder Perlextensively, och det stöds också som ett procedurspråk. För att börja använda det, se till att alla relevanta binära paket för din distro är installerade (exempel “postgresql-plperl-nn” för Debian) och installera tillägget “plperl”.

Här är en funktion skriven 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;

Full dokumentation här.

Tcl

Tcl är ännu en PL som stöds av kärnan Postgres. Här är ett exempel:

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;

För mer information, se dokumenten här.

Icke-core procedurspråk

Utöver dessa språk finns det projekt med öppen källkod som utvecklar och underhåller stöd för andra som Java, Lua, R etc.

Det finns en lista här:https://www.postgresql.org/docs/current/external-pl.html

Aggregerade funktioner

Aggregatfunktioner fungerar över en uppsättning värden och returnerar ett enda resultat.PostgreSQL har ett gäng inbyggda aggregerade funktioner (se en fullständig lista här). För att till exempel få populationens standardavvikelse för alla värden i en kolumn, kan:

SELECT stddev_pop(grade) FROM students;

Du kan definiera dina egna aggregerade funktioner som beter sig på liknande sätt. Auser-definierat aggregat sammanställs från ett fåtal individuella fristående funktioner som fungerar på det interna tillståndet (till exempel kan det interna tillståndet för ett aggregat som beräknar medelvärde vara "summa" och "räkne"-variabler).

Här är ett användardefinierat aggregat som beräknar medianen för en uppsättning värden:

-- 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 anropas som:

SELECT median(grade) FROM students;

Mer information finns i dokumenten om aggregat och CREATE AGGREGATE-satsen.

Användardefinierade typer

De delade biblioteken skrivna i C som vi såg tidigare kan inte bara definiera funktioner utan även datatyper. Dessa användardefinierade typer kan användas som datatyper för kolumner precis som de inbyggda typerna. Du kan definiera funktioner för att arbeta med värdena för dina användardefinierade typer.

Det krävs lite kod för att definiera en ny typ. Se dokumenten här som leder dig genom att skapa en ny typ för att representera komplexa tal. Postgres-källan innehåller också handledningskod för detta.

Operatorer

Operatörer gör funktionerna enklare att använda (till exempel skriva 1 + 2 snarare än sum(1, 2) ), och du kan definiera operatorer för användardefinierade typer med CREATE OPERATOR-satsen.

Här är ett exempel för att skapa en + operator som mappar till funktionencomplex_add som lägger till två complex siffror:

CREATE OPERATOR + (
    leftarg = complex,
    rightarg = complex,
    function = complex_add,
    commutator = +
);

Mer information här och här.

Operatorklasser och operatörsfamiljer

Operatörsklasser låter din datatyp fungera med det inbyggda B-Tree och andra indexeringsmetoder. Till exempel, om du vill skapa ett B-Tree-index på en kolumn av typen "komplex", måste du berätta för Postgres hur man jämför två värden av denna typ för att avgöra om det ena är mindre, lika med eller större än det andra.

Det skulle vara bra för komplexa typer att jämföras med heltal eller flytande poäng, vilket är där operatörsfamiljer kommer in.

Du kan läsa allt om operatörsklasser och familjer här.

Triggers

Triggers är en kraftfull mekanism för att skapa biverkningar för normala operationer, även om de kan vara farliga om de överanvänds eller missbrukas. I huvudsak kopplar utlösare händelser till funktioner. Funktionen som refereras till kan anropas:

  • före eller efter infogning/uppdatering/borttagning av en rad i en tabell
  • vid trunkering av en tabell
  • istället för att infoga/uppdatera/ta bort en rad i en vy

Funktionen kan anropas för varje rad som påverkas av ett uttalande, eller en gång perstatement. Och det finns ännu fler saker, som kaskad av triggers, som alla förklaras här.

Triggerfunktioner kan skrivas i C, eller i någon av PL-funktionerna, men inte iSQL. Här är ett exempel för att infoga en rad i en revision tabell, för varje uppdatering som görs till priset av ett objekt .

-- 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 allt om triggers här, tillsammans med CREATE TRIGGER-dokumentationen.

Händelseutlösare

Medan utlöser svara på DML-händelser på en enda tabell, händelseutlösare kan svara på DDL-händelser i en viss databas. Händelser inkluderar skapa, ändra, släppa en mängd olika objekt, som tabeller, index, scheman, vyer, funktioner, typer, operatorer etc.

Här är en händelseutlösare som förhindrar att objekt släpps från "audit"-schemat:

-- 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();

Mer information kan hittas här och i CREATE EVENT TRIGGER-dokumentationen.

Regler

PostgreSQL kommer med en funktion som låter dig skriva om frågor innan den tar sig till frågeplaneraren. Åtgärden liknar något som att konfigurera Nginx eller Apache för att skriva om en inkommande URL innan den bearbetas.

Här är två exempel som påverkar INSERT-satser på en tabell och gör dem till något annat:

-- 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);

Det här kapitlet från dokumentationen har mer information om regler.

Lagrade procedurer

Från och med Postgres 11 är det möjligt att skapa lagrade procedurer Jämfört med lagrade funktioner finns det bara en extra sak som procedurer kan göra – transaktionskontroll.

Här är ett exempel:

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 här och här för mer information.

Andra exotiska saker

Utländska dataomslag

Foreign Data Wrappers (FDW) låter dig prata med andra datakällor, som en annan Postgres-server, MySQL, Oracle, Cassandra och mer. All logik för att komma åt den främmande servern är skriven i C, som ett delat bibliotek.

Det finns till och med en kolumnbutik som heter cstore_fdwbased on FDW.

Du kan hitta en lista över FDW-implementering i Postgres Wikian och mer dokumentation här.

Indexåtkomstmetoder

PostgreSQL kommer med indextyper som B-Tree, hash, GIN och mer. Det är möjligt att skriva en egen indextyp liknande denna, som ett delat C-bibliotek. Mer information här.

Tabellåtkomstmetoder

Med den kommande PostgreSQL 12 kommer det att vara möjligt att skapa din egen datalagringsstruktur. Genom att implementera gränssnittet som beskrivs här kan du lagra de dubbla data fysiskt på disken på det sätt du väljer.

Logiska replikeringsplugins

I PostgreSQL implementeras logisk replikering genom att "avkoda" innehållet i WAL-loggen till ett godtyckligt format (som SQL-text eller json) och publiceras till prenumeranter över replikeringsplatser. Denna avkodning görs vialogisk avkodningsutgångsplugin , som kan implementeras som ett delat C-bibliotek som beskrivs här. Det delade biblioteket "test_decoding" är ett sådant plugin, och du kan bygga ditt eget.

Procedurell språkhanterare

Du kan också lägga till stöd för ditt favoritprogrammeringsspråk som en Postgres PL genom att skapa en hanterare – igen som ett delat C-bibliotek. Kom igång här för att skapa PL/Go eller PL/Rust!

Tillägg

Tillägg är Postgres sätt att hantera paket. Säg att du har en C-funktion som gör något användbart, och ett par SQL-satser som gör nödvändiga "CREATE FUNCTION"-satser för att ställa in den. Du kan bunta ihop dessa som en "tillägg" som Postgres kan installera (och avinstallera) i ett enda steg (genom att anropa "SKAPA TILLÄGGNING"). När du lägger ut en ny version kan du även inkludera uppgraderingssteg i tillägget.

Även om det inte är programmering på serversidan i sig, är tillägg standard och mycket effektivt sätt att paketera och distribuera din serverkod.

Mer information om tillägg finns här och här.


  1. Salesforce SOQL från SQL Server

  2. Java JDBC - Hur man ansluter till Oracle med tnsnames.ora

  3. Hur man exporterar data till CSV-fil i Oracle med PL SQL-proceduren

  4. Vad är PLSQL-poster i Oracle