3 min read

Fremdtabellen in PostgreSQL

Fremdtabellen in PostgreSQL
Photo by Tabsshotit / Unsplash

Wenn ich als Entwickler eine Fremdtabelle einbinden möchte, weis ich ungefähr, was zu tun ist, komme aber jedes mal wieder ins schleudern mit den Details. Deswegen habe ich beschlossen, das Thema einmal darzustellen. Ich schreibe mir also eigentlich meinen eigenen Leitfaden.

Zunächst einmal lege ich zwei User (roles) mit eingeschränkten Rechten an. Das muss natürlich unter dem Account eines Superusers erfolgen:

CREATE ROLE linktst1 WITH
	LOGIN
	NOSUPERUSER
	CREATEDB
	CREATEROLE
	PASSWORD 'linktst';

CREATE ROLE linktst2 WITH
	LOGIN
	NOSUPERUSER
	CREATEDB
	CREATEROLE
	PASSWORD 'linktst';

Dann können die beiden User linktst1 und linktst2 je eine Datenbank link1 und link2 anlegen:

CREATE DATABASE link1
    WITH
    OWNER = linktst1
    ENCODING = 'UTF8'
    LOCALE_PROVIDER = 'libc'
    CONNECTION LIMIT = -1
    IS_TEMPLATE = False;

CREATE DATABASE link2
    WITH
    OWNER = linktst1
    ENCODING = 'UTF8'
    LOCALE_PROVIDER = 'libc'
    CONNECTION LIMIT = -1
    IS_TEMPLATE = False;

Für den User link1 legen wir jetzt eine Tabelle an und befüllen sie mit ein paar Werten:

CREATE TABLE TEST
(
    id integer,
    text character varying COLLATE pg_catalog."default"
);

INSERT INTO TEST VALUES (1, 'test 1');
INSERT INTO TEST VALUES (2, 'test 2');
INSERT INTO TEST VALUES (3, 'test 3');

Dann müssen Foreign Data Wrappers angelegt werden. Das kann nur mit einem Superuser gemacht werden:

CREATE EXTENSION postgres_fdw;

CREATE FOREIGN DATA WRAPPER postgres_fdw
    VALIDATOR public.postgres_fdw_validator
    HANDLER public.postgres_fdw_handler;

ALTER FOREIGN DATA WRAPPER postgres_fdw
    OWNER TO postgres;

CREATE SERVER link1_svr
    FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (dbname 'link1', host '192.168.xxx.xxx', port '5432');

Die Foreign Data Wrapper werden in der Postgres-Instanz abgelegt. Jede Datenbank hat aber ihre eigenen Referenz auf diese Wrapper. Das führt dazu, dass, die Definition wie oben angegeben nur richtig ist, wenn der spezifische FOREIGN DATA WRAPPER in der Instanz noch nicht definiert wurde. Gibt es den WRAPPER schon, dann reicht es die CREATE EXTENSION-Anweisung auszuführen, weil sie dafür sorgt, das die Referenztabelle automatisch befüllt wird. Ein folgendes CREATE FOREIGN DATA WRAPPER gibt dann einen Fehlerhinweis aus, der besagt, dass es den Wrapper schon gibt.

Jetzt können unter dem User linktst2, der Server, das Use Mapping und die Fremdtabelle definiert werden:

CREATE SERVER linked_server
    FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (dbname 'link1', host '192.168.xxx.xxx', port '5432');

CREATE USER MAPPING FOR linktst2 SERVER linked_server
    OPTIONS (password 'linktst', "user" 'linktst1');

CREATE FOREIGN TABLE IF NOT EXISTS public.test
(
    id integer,
    text character varying COLLATE pg_catalog."default"
)
    SERVER be_as1
    OPTIONS (schema_name 'public', table_name 'test');

select * from public.test;

Die letzte Zeile verifiziert dann den Zugriff und zeigt die eben eingefügten Daten an:

 id |  text
----+--------
  1 | test 1
  2 | test 2
  3 | test 3
(3 Zeilen)

Da der FOREIGN DATA WRAPPER automatisch nach der CREATE EXTENSION-Anweisung zur Verfügung steht, falls er schon in der Postgres-Instanz enthalten war, können die Anweisungen für den DATA WRAPPER auch konditionalisiert werden. Dann würde der obige Abschnitt folgendermaßen aussehen:

CREATE EXTENSION postgres_fdw;

DO $$
BEGIN
    IF (SELECT count(*) from pg_foreign_data_wrapper where fdwname = 'postgres_fdw') < 1 THEN
        CREATE FOREIGN DATA WRAPPER postgres_fdw
            VALIDATOR public.postgres_fdw_validator
            HANDLER public.postgres_fdw_handler;
        ALTER FOREIGN DATA WRAPPER postgres_fdw
            OWNER TO postgres;
    ELSE
        RAISE NOTICE 'Der foreign_data_wrapper existiert bereits';
   END IF;
END; $$;

CREATE SERVER link1_svr
    FOREIGN DATA WRAPPER postgres_fdw
    OPTIONS (dbname 'link1', host '192.168.xxx.xxx', port '5432');

Referenzen

  1. https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html
  2. https://www.postgresql.org/docs/current/sql-createextension.html
Skript mit wechselnden ROLEs unter PostgreSQL ausführen
Dann und wann wird eine Skript benötigt, welches abschnittsweise unter verschiendenen ROLEs (Datenbank-Usern) ausgeführt wird. Dazu kann man die Datenbankverbindung in psql mit Username und Datenbank á la \c <database> <role> neu herstellen. Es wäre unsicher und damit sehr unschön, wenn dazu alle benötigten Passworte im Skript auftauchen würden. Genau hierfür gibt</role></database>