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

PostgreSQL:Wie setze ich den Suchpfad innerhalb einer Funktion?

Generische Lösung

Ich habe eine reine SQL-Funktion mit set_config() erstellt.

Diese Lösung unterstützt das Festlegen mehrerer Schemas in einer durch Kommas getrennten Zeichenfolge. Standardmäßig gilt die Änderung für die aktuelle Sitzung. Wenn Sie den Parameter „is_local“ auf „true“ setzen, wird die Änderung nur auf die aktuelle Transaktion angewendet, siehe http://www.postgresql.org/docs/9.4/static/functions-admin.html für weitere Details.

CREATE OR REPLACE FUNCTION public.set_search_path(path TEXT, is_local BOOLEAN DEFAULT false) RETURNS TEXT AS $$
    SELECT set_config('search_path', regexp_replace(path, '[^\w ,]', '', 'g'), is_local);
$$ LANGUAGE sql;

Da wir kein dynamisches SQL ausführen, sollte die Wahrscheinlichkeit einer SQL-Injektion geringer sein. Nur um sicherzugehen, habe ich den Text etwas naiv bereinigt, indem ich alle Zeichen außer Alphanumerik, Leerzeichen und Komma entfernt habe. Den String zu maskieren/zu zitieren war nicht trivial, aber ich bin kein Experte, also.. =)

Denken Sie daran, dass es keine Rückmeldung gibt, wenn Sie einen fehlerhaften Pfad festlegen.

Hier ist ein Beispielcode zum Testen:

DROP SCHEMA IF EXISTS testschema CASCADE;
CREATE SCHEMA testschema;
CREATE TABLE testschema.mytable ( id INTEGER );

SELECT set_search_path('testschema, public');
SHOW search_path;

INSERT INTO mytable VALUES(123);
SELECT * FROM mytable;

Ein Test, der auf dem Originalcode von OP basiert

Da wir das Schema für mytable nicht im Voraus kennen, müssen wir dynamisches SQL verwenden. Ich habe den set_config-Oneliner in die get_sections()-Funktion eingebettet, anstatt die generische Funktion zu verwenden.

Hinweis: Ich musste is_local=false in set_config() setzen, damit dies funktionierte. Das bedeutet, dass der geänderte Pfad nach Ausführung der Funktion erhalten bleibt. Ich bin mir nicht sicher warum.

DROP SCHEMA IF EXISTS testschema CASCADE;
CREATE SCHEMA testschema;
SET search_path TO public;

CREATE TABLE testschema.mytable ( id INTEGER, name varchar, type varchar );
INSERT INTO testschema.mytable VALUES (123,'name', 'some-type');
INSERT INTO testschema.mytable VALUES (567,'name2', 'beer');

CREATE OR REPLACE FUNCTION get_sections(schema_name TEXT) RETURNS 
TABLE(id integer, name varchar, type varchar) AS $$
BEGIN
    PERFORM set_config('search_path', regexp_replace(schema_name||', public', '[^\w ,]', '', 'g'), true);
    EXECUTE 'SELECT id, name, type FROM mytable';
END;
$$ LANGUAGE plpgsql;

SET search_path TO public;
SELECT * FROM get_sections('testschema');
SHOW search_path;  -- Unfortunately this has modified the search_path for the whole session.