PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Überblick über die serverseitige Programmierung in PostgreSQL

Es gibt viele Möglichkeiten, wie Sie den Postgres-Server dazu bringen können, vordefinierten Code auszuführen. Nachfolgend finden Sie eine umfassende Liste mit Beispielen, wie Sie den Postgres-Server vordefinierte Logik speichern lassen können, die Sie später in Ihrer Anwendung verwenden können.

SQL-Funktionen

Mit Postgres können Sie „benutzerdefinierte Funktionen“ erstellen, bei denen der Funktionskörper in einer unterstützten Sprache geschrieben werden kann. „SQL-Funktionen“ sind benutzerdefinierte Funktionen, die in regulärem SQL geschrieben sind, was die einfachste Möglichkeit darstellt, komplexe Abfragen und Sequenzen von SQL-Anweisungen zu kapseln.

Hier sind ein paar Beispiele:

-- 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-Funktionen können Basistypen, zusammengesetzte Typen und Zeilen akzeptieren und zurückgeben. Sie unterstützen auch eine variable Anzahl von Argumenten, Standardwerte für Argumente und polymorphe Argumente. Sie können sogar mehrere Zeilen zurückgeben und ein SELECT aus einer Tabelle nachahmen. Es ist auch nicht erforderlich, dass sie überhaupt etwas zurücksenden.

Der Funktionskörper kann jedoch nur SQL-Anweisungen enthalten. Das heißt, es gibt keine Flusskontrollanweisungen (if, while, …), Variablen und dergleichen.

Der Befehl CREATE FUNCTION wird verwendet, um die Funktion zu erstellen. Wie üblich können Sie sie ALTERund DROPen.

Dies ist ein großartiger Ort, um weiter zu graben:https://www.postgresql.org/docs/current/xfunc-sql.html

C-Funktionen

Während SQL-Funktionen am einfachsten zu schreiben und am wenigsten leistungsfähig sind, können am anderen Ende des Spektrums Funktionen in C geschrieben werden und können so ziemlich alles. Solche Funktionen müssen in C codiert und als gemeinsam genutzte Bibliothek erstellt werden, die von Postgres dynamisch geladen werden kann.

Sie müssen Postgres mitteilen, wo die gemeinsam genutzte Bibliothek geladen werden soll, sowie den Namen und die Signatur der Funktion:

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

Dies besagt, dass die gemeinsam genutzte Bibliothek myfuncs.so , das in einem vordefinierten Suchpfad vorhanden ist, enthält Einstiegspunkte, die von Postgres aufgerufen werden können, wobei einer der Einstiegspunkte „sum“ ist, der als Funktion aufgerufen werden kann.

Der eigentliche Code in C wäre zu lang, um ihn hier aufzunehmen, aber Sie können alles darüber in der Dokumentation nachlesen. In Kombination mit dem Server Programming Interface (SPI) ist es möglich, fast jede Operation auszuführen, die Sie auf andere Weise ausführen können.

Mit den hier definierten C-Funktionen können Sie beispielsweise HTTP-Anforderungen ausführen:

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

Es ist auch möglich, solche gemeinsam genutzten Bibliotheken in anderen Sprachen wie C++ oder Go zu schreiben, die gemeinsam genutzte Bibliotheken mit „C“-Verknüpfungen erstellen können.

PL/pgSQL-Funktionen

Außer in SQL und C können Sie Funktionen in prozeduralen Sprachen schreiben . Vier solcher Sprachen werden vom PostgreSQL-Kern unterstützt – pgSQL, Python, Perl und Tcl. Die Unterstützung für jede prozedurale Sprache selbst stammt aus einer gemeinsam genutzten C-Bibliothek und funktioniert ähnlich wie mod_perl oder mod_python aus der Apache-Ära.

pgSQL ist die kanonische, am häufigsten verwendete, SQL-ähnliche Sprache, in der gespeicherte Funktionen für PostgreSQL geschrieben sind. Es ist standardmäßig verfügbar, da es in template1 installiert ist .

PL/pgSQL ist eine vollwertige Sprache mit Variablen, Ausdrücken und Kontrollanweisungen; und enthält Funktionen wie Cursor, um insbesondere mit SQL-Daten zu arbeiten. Es ist hier ausführlich dokumentiert.

Hier ist ein Beispiel:

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 prozedurale Kernsprachen

Die anderen prozeduralen Sprachen – Python, Perl, Tcl – ermöglichen es Entwicklern, eine Sprache zu verwenden, mit der sie bereits vertraut sind. Obwohl die Unterstützung für diese Sprachen im Postgres-Quellbaum vorhanden ist, installieren Distributionen die Binärdateien normalerweise nicht standardmäßig. Beispielsweise müssen Sie in Debian Folgendes tun:

sudo apt install postgresql-plpython-11

um die PL/Python-Unterstützung für PostgreSQL 11 zu installieren.

Unabhängig davon, in welcher Sprache Sie eine Funktion schreiben, nimmt der Aufrufer keine Unterschiede in der Verwendung wahr.

Python

Die PL/Python-Erweiterung unterstützt Schreibfunktionen in Python 2 und Python 3. Gehen Sie wie folgt vor, um sie zu installieren:

CREATE EXTENSION plpythonu;

Hier ist eine in PL/Python geschriebene Funktion:

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

Die Python-Umgebung, in der der Funktionsrumpf ausgeführt wird, hat ein Modul namens plpy automatisch darin importiert. Dieses Modul enthält Methoden, mit denen Sie Abfragen vorbereiten und ausführen, Transaktionen verarbeiten und mit Cursorn arbeiten können.

Weitere Informationen finden Sie in Kapitel 46 der Postgres-Dokumentation.

Perl

Nun ja, Perl. Postgres-Entwicklungs-, Test- und Build-Prozesse verwenden Perle ausgiebig und es wird auch als prozedurale Sprache unterstützt. Um es zu verwenden, vergewissern Sie sich, dass alle relevanten Binärpakete für Ihre Distribution installiert sind (Beispiel „postgresql-plperl-nn“ für Debian) und installieren Sie die Erweiterung „plperl“.

Hier ist eine in PL/Perl geschriebene Funktion:

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;

Vollständige Dokumentation hier.

Tcl

Tcl ist eine weitere PL, die von Core Postgres unterstützt wird. Hier ist ein Beispiel:

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;

Weitere Informationen finden Sie in der Dokumentation hier.

Prozedurale Nicht-Kernsprachen

Über diese Sprachen hinaus gibt es Open-Source-Projekte, die Unterstützung für andere wie Java, Lua, R usw. entwickeln und pflegen.

Eine Liste gibt es hier:https://www.postgresql.org/docs/current/external-pl.html

Aggregatfunktionen

Aggregatfunktionen arbeiten mit einer Reihe von Werten und geben ein einzelnes Ergebnis zurück. PostgreSQL verfügt über eine Reihe integrierter Aggregatfunktionen (eine vollständige Liste finden Sie hier). Um beispielsweise die Populationsstandardabweichung aller Werte in einer Spalte zu erhalten, müssen Sie kann:

SELECT stddev_pop(grade) FROM students;

Sie können Ihre eigenen Aggregatfunktionen definieren, die sich ähnlich verhalten. Ein auserdefiniertes Aggregat wird aus einigen einzelnen eigenständigen Funktionen zusammengesetzt, die auf dem internen Zustand arbeiten (zum Beispiel könnte der interne Zustand eines Aggregats, das den Durchschnitt berechnet, „Summe“- und „Zähl“-Variablen sein).

Hier ist ein benutzerdefiniertes Aggregat, das den Median einer Reihe von Werten berechnet:

-- 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 aufgerufen werden kann als:

SELECT median(grade) FROM students;

Weitere Informationen finden Sie in der Dokumentation zu Aggregaten und der CREATE AGGREGATE-Anweisung.

Benutzerdefinierte Typen

Die zuvor in C geschriebenen gemeinsam genutzten Bibliotheken können nicht nur Funktionen, sondern auch Datentypen definieren. Diese benutzerdefinierten Typen können genau wie die integrierten Typen als Datentypen für Spalten verwendet werden. Sie können Funktionen definieren, um mit den Werten Ihrer benutzerdefinierten Typen zu arbeiten.

Es braucht ein bisschen Code, um einen neuen Typ zu definieren. Sehen Sie sich die Dokumentation hier an, die Sie durch die Erstellung eines neuen Typs zur Darstellung komplexer Zahlen führt. Die Postgres-Quelle enthält auch Tutorial-Code dafür.

Operatoren

Operatoren erleichtern die Verwendung von Funktionen (z. B. das Schreiben von 1 + 2 statt sum(1, 2) ), und Sie können Operatoren für benutzerdefinierte Typen mit der CREATE OPERATOR-Anweisung definieren.

Hier ist ein Beispiel zum Erstellen eines + Operator, der der Funktion complex_add zugeordnet ist das fügt zwei complex hinzu Nummern:

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

Weitere Informationen hier und hier.

Operatorklassen und Operatorfamilien

Operatorklassen lassen Ihren Datentyp mit dem eingebauten B-Tree und anderen Indizierungsmethoden arbeiten. Wenn Sie beispielsweise einen B-Tree-Index für eine Spalte vom Typ „komplex“ erstellen möchten, müssen Sie Postgres mitteilen, wie zwei Werte dieses Typs verglichen werden sollen, um festzustellen, ob einer kleiner, gleich oder größer als der andere ist /P>

Es wäre gut, komplexe Typen mit Ganzzahlen oder Gleitkommawerten zu vergleichen, und hier kommen Operatorfamilien ins Spiel.

Hier können Sie alles über Operatorklassen und -familien nachlesen.

Auslöser

Auslöser sind ein leistungsfähiger Mechanismus zum Erzeugen von Nebenwirkungen für normale Operationen, obwohl sie gefährlich sein können, wenn sie überbeansprucht oder missbraucht werden. Im Wesentlichen verbinden Trigger Ereignisse mit Funktionen. Die genannte Funktion kann aufgerufen werden:

  • vor oder nach dem Einfügen/Aktualisieren/Löschen einer Zeile einer Tabelle
  • beim Abschneiden einer Tabelle
  • statt Einfügen/Aktualisieren/Löschen einer Zeile einer Ansicht

Die Funktion kann für jede von einer Anweisung betroffene Zeile oder einmal pro Anweisung aufgerufen werden. Und es gibt noch mehr Dinge, wie das Kaskadieren von Triggern, die alle hier erklärt werden.

Triggerfunktionen können in C oder in einer der PL-Funktionen geschrieben werden, aber nicht in SQL. Hier ist ein Beispiel zum Einfügen einer Zeile in ein Audit Tabelle, für jede Aktualisierung des Preises eines Artikels .

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

Lesen Sie hier alles über Trigger, zusammen mit der CREATE TRIGGER-Dokumentation.

Ereignisauslöser

Während triggert Reagieren auf DML-Ereignisse in einer einzelnen Tabelle, Ereignisauslöser kann auf DDL-Ereignisse in einer bestimmten Datenbank reagieren. Zu den Ereignissen gehören das Erstellen, Ändern und Löschen einer Vielzahl von Objekten wie Tabellen, Indizes, Schemas, Ansichten, Funktionen, Typen, Operatoren usw.

Hier ist ein Ereignisauslöser, der das Löschen von Objekten aus dem „Audit“-Schema verhindert:

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

Weitere Informationen finden Sie hier und in der CREATE EVENT TRIGGER-Dokumentation.

Regeln

PostgreSQL verfügt über eine Funktion, mit der Sie Abfragen umschreiben können, bevor sie zum Abfrageplaner gelangen. Die Operation ähnelt in gewisser Weise der Konfiguration von Nginx oder Apache, um eine eingehende URL vor der Verarbeitung neu zu schreiben.

Hier sind zwei Beispiele, die sich auf INSERT-Anweisungen in einer Tabelle auswirken und sie dazu bringen, etwas anderes zu tun:

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

Dieses Kapitel der Dokumentation enthält weitere Informationen zu Regeln.

Gespeicherte Prozeduren

Ab Postgres 11 ist es möglich, gespeicherte Prozeduren zu erstellen Auch.Im Vergleich zu gespeicherten Funktionen gibt es nur eine zusätzliche Sache, die Prozeduren tun können – Transaktionskontrolle.

Hier ist ein Beispiel:

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

Weitere Informationen finden Sie hier und hier.

Andere exotische Dinge

Wrapper für fremde Daten

Foreign Data Wrapper (FDWs) ermöglichen Ihnen die Kommunikation mit anderen Datenquellen, wie einem anderen Postgres-Server, MySQL, Oracle, Cassandra und mehr. Die gesamte Logik für den Zugriff auf den fremden Server ist in C als gemeinsam genutzte Bibliothek geschrieben.

Es gibt sogar einen auf FDW basierenden Säulenspeicher namens cstore_fdw.

Eine Liste der FDW-Implementierung finden Sie im Postgres-Wiki und weitere Dokumentation hier.

Indexzugriffsmethoden

PostgreSQL enthält Indextypen wie B-Tree, Hash, GIN und mehr. Es ist möglich, einen ähnlichen Indextyp wie diesen als C-Shared-Library zu schreiben. Weitere Details hier.

Tabellenzugriffsmethoden

Mit dem kommenden PostgreSQL 12 wird es möglich sein, eine eigene Datenspeicherstruktur zu erstellen. Indem Sie die hier beschriebene Schnittstelle implementieren, können Sie die Tupeldaten auf die von Ihnen gewählte Weise physisch auf der Festplatte speichern.

Plug-ins für die logische Replikation

In PostgreSQL wird die logische Replikation implementiert, indem der Inhalt des Write-Ahead-Protokolls (WAL) in ein beliebiges Format (wie SQL-Text oder json) „dekodiert“ und über Replikationsslots für Abonnenten veröffentlicht wird. Diese Dekodierung erfolgt über das Plug-In für die logische Dekodierung , die wie hier beschrieben als gemeinsam genutzte C-Bibliothek implementiert werden kann. Die gemeinsam genutzte Bibliothek „test_decoding“ ist ein solches Plugin, und Sie können Ihr eigenes erstellen.

Handler für prozedurale Sprachen

Sie können auch Unterstützung für Ihre bevorzugte Programmiersprache als Postgres-PL hinzufügen, indem Sie einen Handler erstellen – wiederum als gemeinsam genutzte C-Bibliothek. Beginnen Sie damit, PL/Go oder PL/Rust zu erstellen!

Erweiterungen

Erweiterungen sind die Paketverwaltung von Postgres. Angenommen, Sie haben eine C-Funktion, die etwas Nützliches tut, und ein paar SQL-Anweisungen, die die notwendigen „CREATE FUNCTION“-Anweisungen machen, um sie einzurichten. Sie können diese als „Erweiterung“ bündeln, die Postgres in einem einzigen Schritt installieren (und deinstallieren) kann (durch Aufrufen von „CREATE EXTENSION“). Wenn Sie eine neue Version veröffentlichen, können Sie auch Upgrade-Schritte in die Erweiterung aufnehmen.

Erweiterungen sind zwar per se keine serverseitige Programmierung, aber sie sind die standardmäßige und sehr effiziente Möglichkeit, Ihren serverseitigen Code zu packen und zu verteilen.

Weitere Informationen zu Erweiterungen finden Sie hier und hier.