PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

PostgreSQL:como defino o search_path de dentro de uma função?

Solução genérica


Eu criei uma função sql pura usando set_config().

Esta solução oferece suporte à configuração de vários esquemas em uma string separada por vírgulas. Por padrão, a alteração se aplica à sessão atual. Definir o parâmetro "is_local" como true faz com que a alteração se aplique apenas à transação atual, consulte http://www.postgresql.org/docs/9.4/static/functions-admin.html para mais detalhes.
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;

Como não estamos executando nenhum sql dinâmico, deve haver menos chance de injeção de sql. Só para ter certeza, adicionei uma limpeza ingênua do texto, removendo todos os caracteres, exceto alfanuméricos, espaço e vírgula. Escapar/citar a string não foi trivial, mas não sou especialista, então.. =)

Lembre-se de que não há feedback se você definir um caminho malformado.

Aqui está um código de exemplo para teste:
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;

Um teste baseado no código original do OP


Como não conhecemos o esquema de mytable com antecedência, precisamos usar o sql dinâmico. Eu incorporei o set_config-oneliner na função get_sections() em vez de usar a função generic'ish.

Observação: Eu tive que definir is_local=false em set_config() para que isso funcionasse. Isso significa que o caminho modificado permanece após a execução da função. Não tenho certeza 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.