As opções incluem:
-
Quando você abre uma conexão,CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user');
. Em seguida, em seu gatilho,SELECT username FROM current_app_user
para obter o nome de usuário atual, possivelmente como uma subconsulta.
-
Empostgresql.conf
crie uma entrada para um GUC personalizado comomy_app.username = 'unknown';
. Sempre que você criar uma conexão executeSET my_app.username = 'the_user';
. Em seguida, nos gatilhos, use ocurrent_setting('my_app.username')
função para obter o valor. Efetivamente, você está abusando do maquinário GUC para fornecer variáveis de sessão. Leia a documentação apropriada para a versão do seu servidor, pois os GUCs personalizados foram alterados na versão 9.2 .
-
Ajuste seu aplicativo para que ele tenha funções de banco de dados para cada usuário do aplicativo.SET ROLE
para esse usuário antes de fazer o trabalho. Isso não apenas permite que você use ocurrent_user
integrado função tipo variável paraSELECT current_user;
, também permite impor a segurança no banco de dados . Veja esta pergunta. Você pode fazer login diretamente como usuário em vez de usarSET ROLE
, mas isso tende a dificultar o pool de conexões.
Em ambos os três casos em que você está agrupando conexões, você deve ter cuidado para
DISCARD ALL;
quando você retorna uma conexão com o pool. (Embora não esteja documentado, DISCARD ALL
faz um RESET ROLE
). Configuração comum para demonstrações:
CREATE TABLE tg_demo(blah text);
INSERT INTO tg_demo(blah) VALUES ('spam'),('eggs');
-- Placeholder; will be replaced by demo functions
CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
SELECT 'unknown';
$$ LANGUAGE sql;
CREATE OR REPLACE FUNCTION tg_demo_trigger() RETURNS trigger AS $$
BEGIN
RAISE NOTICE 'Current user is: %',get_app_user();
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER tg_demo_tg
AFTER INSERT OR UPDATE OR DELETE ON tg_demo
FOR EACH ROW EXECUTE PROCEDURE tg_demo_trigger();
Usando um GUC:
- Nas
CUSTOMIZED OPTIONS
seção depostgresql.conf
, adicione uma linha comomyapp.username = 'unknown_user'
. Nas versões do PostgreSQL anteriores à 9.2, você também deve definircustom_variable_classes = 'myapp'
. - Reinicie o PostgreSQL. Agora você poderá
SHOW myapp.username
e obtenha o valorunknown_user
.
Agora você pode usar
SET myapp.username = 'the_user';
quando você estabelece uma conexão, ou alternativamente SET LOCAL myapp.username = 'the_user';
depois de BEGIN
ndo uma transação se você quiser que ela seja local de transação, o que é conveniente para conexões em pool. O
get_app_user
definição da função:CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
SELECT current_setting('myapp.username');
$$ LANGUAGE sql;
Demonstração usando
SET LOCAL
para o nome de usuário atual local da transação:regress=> BEGIN;
BEGIN
regress=> SET LOCAL myapp.username = 'test_user';
SET
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE: Current user is: test_user
INSERT 0 1
regress=> COMMIT;
COMMIT
regress=> SHOW myapp.username;
myapp.username
----------------
unknown_user
(1 row)
Se você usar
SET
em vez de SET LOCAL
a configuração não será revertida no momento da confirmação/reversão, portanto, é persistente durante a sessão. Ele ainda é redefinido por DISCARD ALL
:regress=> SET myapp.username = 'test';
SET
regress=> SHOW myapp.username;
myapp.username
----------------
test
(1 row)
regress=> DISCARD ALL;
DISCARD ALL
regress=> SHOW myapp.username;
myapp.username
----------------
unknown_user
(1 row)
Além disso, observe que você não pode usar
SET
ou SET LOCAL
com parâmetros de ligação do lado do servidor. Se você quiser usar parâmetros de ligação ("declarações preparadas"), considere usar o formulário de função set_config(...)
. Veja as funções de administração do sistema Usando uma tabela temporária
Essa abordagem requer o uso de um gatilho (ou função auxiliar chamada por um gatilho, de preferência) que tenta ler um valor de uma tabela temporária que toda sessão deveria ter. Se a tabela temporária não puder ser localizada, um valor padrão será fornecido. Isso provavelmente será um pouco lento . Teste com atenção.
O
get_app_user()
definição:CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
DECLARE
cur_user text;
BEGIN
BEGIN
cur_user := (SELECT username FROM current_app_user);
EXCEPTION WHEN undefined_table THEN
cur_user := 'unknown_user';
END;
RETURN cur_user;
END;
$$ LANGUAGE plpgsql VOLATILE;
Demonstração:
regress=> CREATE TEMPORARY TABLE current_app_user(username text);
CREATE TABLE
regress=> INSERT INTO current_app_user(username) VALUES ('testuser');
INSERT 0 1
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE: Current user is: testuser
INSERT 0 1
regress=> DISCARD ALL;
DISCARD ALL
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE: Current user is: unknown_user
INSERT 0 1
Variáveis de sessão segura
Há também uma proposta para adicionar "variáveis de sessão seguras" ao PostgreSQL. Estas são um pouco como variáveis de pacote. A partir do PostgreSQL 12, o recurso não foi incluído, mas fique de olho e fale na lista de hackers se isso for algo que você precisa.
Avançado:seu próprio ramal com área de memória compartilhada
Para usos avançados, você pode até ter sua própria extensão C para registrar uma área de memória compartilhada e se comunicar entre back-ends usando chamadas de função C que lêem/gravam valores em um segmento DSA. Veja os exemplos de programação PostgreSQL para detalhes. Você precisará de conhecimento em C, tempo e paciência.