Fremdtabellen in PostgreSQL
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
- https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html
- https://www.postgresql.org/docs/current/sql-createextension.html