sql >> Base de Datos >  >> RDS >> PostgreSQL

PostgreSQL:¿cómo configuro search_path desde dentro de una función?

Solución genérica

Creé una función sql pura usando set_config().

Esta solución admite la configuración de varios esquemas en una cadena separada por comas. Por defecto, el cambio se aplica a la sesión actual. Establecer el parámetro "is_local" en verdadero hace que el cambio solo se aplique a la transacción actual, consulte http://www.postgresql.org/docs/9.4/static/functions-admin.html para más detalles.

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;

Dado que no estamos ejecutando ningún sql dinámico, debería haber menos posibilidades de inyección de sql. Solo para estar seguro, agregué un poco de desinfección ingenua del texto al eliminar todos los caracteres excepto los alfanuméricos, el espacio y la coma. Escapar/citar la cadena no fue trivial, pero no soy un experto, así que... =)

Recuerde que no hay comentarios si establece una ruta incorrecta.

Aquí hay un código de muestra para probar:

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;

Una prueba basada en el código original de OP

Dado que no conocemos el esquema de mytable de antemano, necesitamos usar sql dinámico. Incrusté set_config-oneliner en la función get_sections() en lugar de usar la función generic'ish.

Nota: Tuve que establecer is_local=false en set_config() para que esto funcionara. Eso significa que la ruta modificada permanece después de ejecutar la función. No estoy seguro de por qué.

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.