PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL:come faccio a impostare il percorso_ricerca dall'interno di una funzione?

Soluzione generica

Ho creato una pura funzione sql usando set_config().

Questa soluzione supporta l'impostazione di più schemi in una stringa separata da virgole. Per impostazione predefinita, la modifica si applica alla sessione corrente. Se si imposta il parametro "is_local" su true, la modifica si applica solo alla transazione corrente, vedere http://www.postgresql.org/docs/9.4/static/functions-admin.html per maggiori dettagli.

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;

Dal momento che non stiamo eseguendo alcun sql dinamico, dovrebbero esserci meno possibilità di iniezione di sql. Giusto per essere sicuro ho aggiunto un po' di sanificazione ingenua del testo rimuovendo tutti i caratteri tranne alfanumerici, spazio e virgola. Escludere/citare la stringa non è stato banale, ma non sono un esperto, quindi.. =)

Ricorda che non ci sono feedback se imposti un percorso non corretto.

Ecco alcuni esempi di codice per il test:

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 basato sul codice originale di OP

Poiché non conosciamo lo schema per mytable in anticipo, è necessario utilizzare sql dinamico. Ho incorporato set_config-oneliner nella funzione get_sections() invece di usare la funzione generic'ish.

Nota: Ho dovuto impostare is_local=false in set_config() affinché funzionasse. Ciò significa che il percorso modificato rimane dopo l'esecuzione della funzione. Non sono sicuro del perché.

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.