sql >> Database >  >> RDS >> PostgreSQL

Overzicht van server-side programmering in PostgreSQL

Er zijn veel manieren waarop u de Postgres-server vooraf gedefinieerde code kunt laten uitvoeren. Hieronder vindt u een uitgebreide lijst, met voorbeelden, van manieren waarop u de Postgres-server vooraf gedefinieerde logica kunt laten opslaan, die u later vanuit uw toepassing kunt gebruiken.

SQL-functies

Met Postgres kunt u "door de gebruiker gedefinieerde functies" maken, waarbij de hoofdtekst van de functie in een ondersteunde taal kan worden geschreven. "SQL-functies" zijn door de gebruiker gedefinieerde functies die zijn geschreven in gewone SQL, wat de eenvoudigste manier is om complexe query's en reeksen SQL-instructies in te kapselen.

Hier zijn een paar voorbeelden:

-- 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-functies kunnen basistypen, samengestelde typen en rijen accepteren en retourneren. Ze ondersteunen ook variabele aantallen argumenten, standaardwaarden voor argumenten en polymorfe argumenten. Ze kunnen zelfs meerdere rijen retourneren, waarbij ze een SELECT uit een tabel nabootsen. Het is ook niet nodig dat ze iets teruggeven.

De hoofdtekst van de functie kan echter alleen SQL-instructies bevatten. Dit betekent dat er geen flow control statements (if, while, …), variabelen en dergelijke zijn.

De opdracht CREATE FUNCTION wordt gebruikt om de functie te maken. Zoals gewoonlijk kun je ze WIJZIGEN en DROPPEN.

Dit is een geweldige plek om verder te graven:https://www.postgresql.org/docs/current/xfunc-sql.html

C-functies

Hoewel SQL-functies het gemakkelijkst te schrijven en het minst krachtig zijn, aan de andere kant van het spectrum, kunnen functies in C worden geschreven en kunnen ze vrijwel alles doen. Dergelijke functies moeten worden gecodeerd in C en worden gebouwd als een gedeelde bibliotheek die dynamisch kan worden geladen door Postgres.

Je moet Postgres vertellen waar de gedeelde bibliotheek moet worden geladen, de naam en handtekening van de functie:

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

Dit zegt dat de gedeelde bibliotheek myfuncs.so , aanwezig in een vooraf gedefinieerd zoekpad, bevat ingangspunten die door Postgres kunnen worden aangeroepen, waarbij een van de ingangspunten 'som' is die als een functie kan worden aangeroepen.

De eigenlijke code in C zou te lang zijn om hier op te nemen, maar u kunt er alles over lezen in de documenten. In combinatie met de Server Programming Interface (SPI) is het mogelijk om bijna elke bewerking uit te voeren die u op een andere manier kunt doen.

Met de hier gedefinieerde C-functies kunt u bijvoorbeeld HTTP-verzoeken uitvoeren:

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

Het is ook mogelijk om dergelijke gedeelde bibliotheken in andere talen te schrijven, zoals C++ of Go, die gedeelde bibliotheken kunnen bouwen met "C"-koppelingen.

PL/pgSQL-functies

Behalve SQL en C, kunt u functies schrijven in proceduretalen . Vier van dergelijke talen worden ondersteund door PostgreSQL - pgSQL, Python, Perl en Tcl. Ondersteuning voor elke proceduretaal zelf komt uit een gedeelde C-bibliotheek en werkt net als mod_perl of mod_python uit het Apache-tijdperk.

pgSQL is de canonieke, meest gebruikte SQL-achtige taal waarin opgeslagen functies voor PostgreSQL zijn geschreven. Het is standaard beschikbaar, dankzij het feit dat het is geïnstalleerd in template1 .

PL/pgSQL is een volwaardige taal met variabelen, uitdrukkingen en controleverklaringen; en bevat functies zoals cursors om in het bijzonder met SQL-gegevens te werken. Het is hier uitgebreid gedocumenteerd.

Hier is een voorbeeld:

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)

Andere procedurele kerntalen

De andere proceduretalen - Python, Perl, Tcl - stellen ontwikkelaars in staat een taal te gebruiken waarmee ze al vertrouwd zijn. Hoewel ondersteuning voor deze talen in de Postgres-bronstructuur aanwezig is, installeren distributies de binaire bestanden meestal niet standaard. In Debian moet u bijvoorbeeld het volgende doen:

sudo apt install postgresql-plpython-11

om de PL/Python-ondersteuning voor PostgreSQL 11 te installeren.

Welke taal u ook gebruikt om een ​​functie in te schrijven, de beller merkt geen verschillen in het gebruik ervan.

Python

De PL/Python-extensie ondersteunt schrijffuncties in Python 2 en Python 3. Om het te installeren, doe:

CREATE EXTENSION plpythonu;

Hier is een functie geschreven in PL/Python:

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

De Python-omgeving waarin de functietekst draait, heeft een module met de naam plpy automatisch erin geïmporteerd. Deze module bevat methoden waarmee u query's kunt voorbereiden en uitvoeren, transacties kunt afhandelen en met cursors kunt werken.

Meer informatie is te vinden in hoofdstuk 46 van de Postgres-documenten.

Perl

Nou ja, Perl. Postgres-ontwikkelings-, test- en bouwprocessen gebruiken Perlextensively en het wordt ook ondersteund als proceduretaal. Om het te gebruiken, moet u ervoor zorgen dat alle relevante binaire pakketten voor uw distro zijn geïnstalleerd (bijvoorbeeld "postgresql-plperl-nn" voor Debian) en de extensie "plperl" installeren.

Hier is een functie geschreven in 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;

Volledige documentatie hier.

Tcl

Tcl is nog een andere PL die wordt ondersteund door kern Postgres. Hier is een voorbeeld:

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;

Bekijk de documenten hier voor meer informatie.

Niet-core procedurele talen

Naast deze talen zijn er open source projecten die ondersteuning ontwikkelen en onderhouden voor anderen zoals Java, Lua, R etc.

Er is een lijst hier:https://www.postgresql.org/docs/current/external-pl.html

Samengevoegde functies

Geaggregeerde functies werken over een reeks waarden en retourneren een enkel resultaat. PostgreSQL heeft een aantal ingebouwde aggregatiefuncties (zie hier een volledige lijst). Als u bijvoorbeeld de populatiestandaarddeviatie van alle waarden in een kolom wilt krijgen, kan:

SELECT stddev_pop(grade) FROM students;

U kunt uw eigen aggregatiefuncties definiëren die zich op een vergelijkbare manier gedragen. Een door de gebruiker gedefinieerd aggregaat wordt samengesteld uit een paar afzonderlijke zelfstandige functies die werken op de interne status (de interne status van een aggregaat dat het gemiddelde berekent, kan bijvoorbeeld de variabelen 'som' en 'tel' zijn).

Hier is een door de gebruiker gedefinieerd aggregaat dat de mediaan van een reeks waarden berekent:

-- 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='{}'
);

die kan worden aangeroepen als:

SELECT median(grade) FROM students;

Zie de documenten over aggregaten en de instructie CREATE AGGREGATE voor meer informatie.

Door de gebruiker gedefinieerde typen

De gedeelde bibliotheken die in C zijn geschreven en die we eerder zagen, kunnen niet alleen functies definiëren, maar ook gegevenstypen. Deze door de gebruiker gedefinieerde typen kunnen worden gebruikt als gegevenstypen voor kolommen, net als de ingebouwde typen. U kunt functies definiëren om te werken met de waarden van uw door de gebruiker gedefinieerde typen.

Er is een beetje code nodig om een ​​nieuw type te definiëren. Bekijk de documenten hier die u helpen bij het maken van een nieuw type om complexe getallen weer te geven. De Postgres-bron bevat ook zelfstudiecode hiervoor.

Operators

Operators maken functies gemakkelijker te gebruiken (bijvoorbeeld het schrijven van 1 + 2 in plaats van sum(1, 2) ), en u kunt operators voor door de gebruiker gedefinieerde typen definiëren met behulp van de instructie CREATE OPERATOR.

Hier is een voorbeeld om een ​​+ . te maken operator die verwijst naar de functiecomplex_add dat voegt twee complex . toe nummers:

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

Meer informatie hier en hier.

Operatorklassen en operatorfamilies

Operatorklassen laten uw gegevenstype werken met de ingebouwde B-Tree en andere indexeringsmethoden. Als u bijvoorbeeld een B-Tree-index wilt maken op een kolom van het type "complex", moet u Postgres vertellen hoe twee waarden van dit type moeten worden vergeleken om te bepalen of de ene kleiner, gelijk aan of groter is dan de andere.

Het zou goed zijn als complexe typen worden vergeleken met gehele getallen of drijvende-kommawaarden, en dat is waar operatorfamilies om de hoek komen kijken.

Je kunt hier alles lezen over operatorklassen en families.

Triggers

Triggers zijn een krachtig mechanisme voor het creëren van bijwerkingen voor normale operaties, hoewel ze gevaarlijk kunnen zijn als ze te veel worden gebruikt of misbruikt. In wezen verbinden triggers gebeurtenissen met functies. De functie waarnaar wordt verwezen kan worden aangeroepen:

  • voor of na het invoegen/bijwerken/verwijderen van een rij van een tabel
  • op het afkappen van een tabel
  • in plaats van een rij van een weergave invoegen/bijwerken/verwijderen

De functie kan worden aangeroepen voor elke rij die wordt beïnvloed door een instructie, of eenmalig perstatement. En er zijn nog meer dingen, zoals trapsgewijze triggers, die hier allemaal worden uitgelegd.

Triggerfuncties kunnen worden geschreven in C, of ​​in een van de PL-functies, maar niet inSQL. Hier is een voorbeeld om een ​​rij in te voegen in een controle tabel, voor elke update van de prijs van een item .

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

Lees hier alles over triggers, samen met de CREATE TRIGGER-documentatie.

Event-triggers

Terwijl triggers reageren op DML-gebeurtenissen op een enkele tafel, gebeurtenistriggers kan reageren op DDL-gebeurtenissen in een bepaalde database. Gebeurtenissen omvatten het maken, wijzigen en neerzetten van een verscheidenheid aan objecten, zoals tabellen, indexen, schema's, weergaven, functies, typen, operators enz.

Hier is een gebeurtenistrigger die voorkomt dat objecten uit het 'audit'-schema vallen:

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

Meer informatie vindt u hier en in de documentatie CREATE EVENT TRIGGER.

Regels

PostgreSQL wordt geleverd met een functie waarmee u query's kunt herschrijven voordat deze naar de queryplanner gaan. De bewerking lijkt enigszins op het configureren van Nginx of Apache om een ​​inkomende URL te herschrijven voordat deze wordt verwerkt.

Hier zijn twee voorbeelden die van invloed zijn op INSERT-instructies op een tafel en ze iets anders doen:

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

Dit hoofdstuk uit de documentatie bevat meer informatie over regels.

Opgeslagen procedures

Vanaf Postgres 11 is het mogelijk om opgeslagen procedures te maken ook.Vergeleken met opgeslagen functies is er maar één extra ding dat procedures kunnen doen:transactiecontrole.

Hier is een voorbeeld:

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

Zie hier en hier voor meer informatie.

Andere exotische dingen

Foreign Data Wrappers

Met Foreign Data Wrappers (FDW's) kunt u met andere gegevensbronnen praten, zoals een andere Postgres-server, MySQL, Oracle, Cassandra en meer. Alle logica voor toegang tot de buitenlandse server is geschreven in C, als een gedeelde bibliotheek.

Er is zelfs een zuilvormige winkel met de naam cstore_fdw, gebaseerd op FDW.

Je kunt een lijst met FDW-implementaties vinden in de Postgres Wiki en meer documentatie hier.

Indextoegangsmethoden

PostgreSQL wordt geleverd met indextypen zoals B-Tree, hash, GIN en meer. Het is mogelijk om uw eigen indextype vergelijkbaar met dit te schrijven, als een gedeelde C-bibliotheek. Meer details hier.

Tabeltoegangsmethoden

Met de aankomende PostgreSQL 12 is het mogelijk om uw eigen dataopslagstructuur te creëren. Door de hier beschreven interface te implementeren, kunt u de gegevens fysiek op schijf opslaan op de manier van uw keuze.

Logische replicatie-plug-ins

In PostgreSQL wordt logische replicatie geïmplementeerd door de inhoud van het write-ahead log (WAL) te "decoderen" in een willekeurig formaat (zoals SQL-tekst of json) en gepubliceerd naar abonnees via replicatieslots. Deze decodering wordt gedaan via deplug-in voor logische decoderingsuitvoer , die kan worden geïmplementeerd als een gedeelde C-bibliotheek zoals hier beschreven. De gedeelde bibliotheek "test_decoding" is zo'n plug-in en u kunt uw eigen bibliotheek bouwen.

Procedural Language Handler

U kunt ook ondersteuning voor uw favoriete programmeertaal toevoegen als een Postgres PL door een handler te maken - opnieuw als een gedeelde C-bibliotheek. Ga hier aan de slag om PL/Go of PL/Rust te maken!

Extensies

Extensies zijn de manier van pakketbeheer van Postgres. Stel dat je een C-functie hebt die iets nuttigs doet, en een paar SQL-instructies die de noodzakelijke "CREATE FUNCTION"-instructies maken om het in te stellen. U kunt deze bundelen als een "extensie" die Postgres in één enkele stap kan installeren (en de-installeren) (door "EXTENSIE MAKEN" aan te roepen). Wanneer u een nieuwe versie uitbrengt, kunt u ook upgradestappen in de extensie opnemen.

Hoewel het niet per se server-side programmering is, zijn extensies de standaard en zeer efficiënte manier om uw server-side code in te pakken en te distribueren.

Meer informatie over extensies vindt u hier en hier.


  1. Hoe geef ik een wachtwoord op voor niet-interactief 'psql'?

  2. krijg de JOIN-tabel als een reeks resultaten met PostgreSQL/NodeJS

  3. In plaats van trigger in SQL Server verliest SCOPE_IDENTITY?

  4. Serialiseren van verwijdert uit geclusterde Columnstore-indexen