PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL :comment définir le chemin de recherche depuis l'intérieur d'une fonction ?

Solution générique

J'ai créé une fonction sql pure en utilisant set_config().

Cette solution prend en charge la définition de plusieurs schémas dans une chaîne séparée par des virgules. Par défaut, la modification s'applique à la session en cours. Définir le paramètre "is_local" sur true fait que la modification ne s'applique qu'à la transaction en cours, voir http://www.postgresql.org/docs/9.4/static/functions-admin.html pour plus de détails.

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;

Puisque nous n'exécutons aucun sql dynamique, il devrait y avoir moins de chance d'injection sql. Juste pour être sûr, j'ai ajouté une désinfection naïve du texte en supprimant tous les caractères sauf les caractères alphanumériques, l'espace et la virgule. Échapper/citer la chaîne n'était pas trivial, mais je ne suis pas un expert, donc... =)

N'oubliez pas qu'il n'y a pas de retour si vous définissez un chemin mal formé.

Voici un exemple de code à tester :

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;

Un test basé sur le code original d'OP

Comme nous ne connaissons pas le schéma de mytable à l'avance, nous devons utiliser sql dynamique. J'ai intégré le set_config-oneliner dans la fonction get_sections() au lieu d'utiliser la fonction générique.

Remarque : J'ai dû définir is_local=false dans set_config() pour que cela fonctionne. Cela signifie que le chemin modifié reste après l'exécution de la fonction. Je ne sais pas pourquoi.

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.