sql >> Databasteknik >  >> RDS >> PostgreSQL

Skickar användar-ID till PostgreSQL-utlösare

Alternativen inkluderar:

  • När du öppnar en anslutning, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user'); . Sedan i din trigger, SELECT username FROM current_app_user för att få det aktuella användarnamnet, eventuellt som en underfråga.

  • I postgresql.conf skapa en post för en anpassad GUC som my_app.username = 'unknown'; . När du skapar en anslutning kör SET my_app.username = 'the_user'; . Sedan i triggers, använd current_setting('my_app.username') funktion för att få värdet. I själva verket missbrukar du GUC-maskineriet för att tillhandahålla sessionsvariabler. Läs dokumentationen som är lämplig för din serverversion, eftersom anpassade GUC:er ändrades i 9.2 .

  • Justera din applikation så att den har databasroller för varje applikationsanvändare. SET ROLE till den användaren innan arbetet utförs. Detta låter dig inte bara använda den inbyggda current_user variabelliknande funktion till SELECT current_user; , det låter dig också upprätthålla säkerhet i databasen . Se den här frågan. Du kan logga in direkt som användare istället för att använda SET ROLE , men det tenderar att göra anslutningspooling svår.

I båda alla tre fallen du anslutningspooler måste du vara noga med att DISCARD ALL; när du återställer en anslutning till poolen. (Även om det inte är dokumenterat att det gör det, DISCARD ALL gör en RESET ROLE ).

Gemensamma inställningar för demos:

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();

Använda en GUC:

  • I CUSTOMIZED OPTIONS avsnitt av postgresql.conf , lägg till en rad som myapp.username = 'unknown_user' . På PostgreSQL-versioner äldre än 9.2 måste du också ställa in custom_variable_classes = 'myapp' .
  • Starta om PostgreSQL. Du kommer nu att kunna SHOW myapp.username och hämta värdet unknown_user .

Nu kan du använda SET myapp.username = 'the_user'; när du upprättar en anslutning, eller alternativt SET LOCAL myapp.username = 'the_user'; efter BEGIN ning en transaktion om du vill att den ska vara transaktionslokal, vilket är bekvämt för poolade anslutningar.

get_app_user funktionsdefinition:

CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
    SELECT current_setting('myapp.username');
$$ LANGUAGE sql;

Demo med SET LOCAL för transaktionslokalt aktuellt användarnamn:

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)

Om du använder SET istället för SET LOCAL inställningen kommer inte att återställas vid commit/back-tid, så den är beständig under hela sessionen. Den är fortfarande återställd av 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)

Observera också att du inte kan använda SET eller SET LOCAL med bindningsparametrar på serversidan. Om du vill använda bindningsparametrar ("prepared statements"), överväg att använda funktionsformuläret set_config(...) . Se systemadministrationsfunktioner

Använda en tillfällig tabell

Detta tillvägagångssätt kräver användning av en trigger (eller hjälpfunktion som helst anropas av en trigger) som försöker läsa ett värde från en temporär tabell som varje session ska ha. Om den temporära tabellen inte kan hittas, tillhandahålls ett standardvärde. Det här kommer sannolikt att gå något långsamt . Testa noggrant.

get_app_user() definition:

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;

Demo:

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

Säker sessionsvariabler

Det finns också ett förslag om att lägga till "säkra sessionsvariabler" till PostgreSQL. Dessa är lite som paketvariabler. Från och med PostgreSQL 12 har funktionen inte inkluderats, men håll utkik och säg till på hackerlistan om detta är något du behöver.

Avancerat:ditt eget tillägg med delat minnesområde

För avancerad användning kan du till och med ha ditt eget C-tillägg registrera ett delat minnesområde och kommunicera mellan backends med C-funktionsanrop som läser/skriver värden i ett DSA-segment. Se PostgreSQL-programmeringsexemplen för detaljer. Du behöver C-kunskap, tid och tålamod.



  1. SQLite JSON_SET()

  2. RMAN Backup-kommandon

  3. Undantag i huvudtråden java.sql.SQLEUndantag:Åtkomst nekad för användare ''@'localhost' (med lösenord:NO)

  4. Hitta överlappande datumintervall i PostgreSQL