14

Vorrei aggiungere un vincolo che verificherà i valori dalla tabella correlata.CONSTRAINT per verificare i valori da una tabella correlata in remoto (tramite join, ecc.)

Ho 3 tabelle:

CREATE TABLE somethink_usr_rel (
    user_id BIGINT NOT NULL, 
    stomethink_id BIGINT NOT NULL 
); 

CREATE TABLE usr (
    id BIGINT NOT NULL, 
    role_id BIGINT NOT NULL 
); 

CREATE TABLE role (
    id BIGINT NOT NULL, 
    type BIGINT NOT NULL 
); 

(Se si vuole che metta vincolo con l'FK fatemelo sapere.)

voglio aggiungere un vincolo per somethink_usr_rel che controlla type a role (" due tavoli più in là "), ad esempio:

ALTER TABLE somethink_usr_rel 
    ADD CONSTRAINT CH_sm_usr_type_check 
    CHECK (usr.role.type = 'SOME_ENUM'); 

ho provato a fare questo con JOIN s, ma non è riuscito. Qualche idea su come ottenerlo?

risposta

16

CHECK vincoli non possono attualmente fare riferimento ad altri tavoli. Per documentation:

Attualmente, CHECK espressioni non possono contenere subquery né consultare variabili diverse colonne della riga corrente.

Un modo è utilizzare un trigger come demonstrated by @Wolph.

Un pulito soluzione senza trigger (che è più robusto per applicare l'integrità referenziale) sarebbe di aggiungere colonne di ridondanza e includerli nei vincoli FK. Considerate questa risposta strettamente correlati sul dba.SE con le istruzioni dettagliate:

Un'altra opzione sarebbe quella di "falso" una funzione IMMUTABILE fare il controllo e l'uso che in un vincolo CHECK. Postgres lo permetterà, ma sii consapevole delle possibili conseguenze. È meglio creare un vincolo NOT VALID. Dettagli:

7

Un vincolo CHECK non è un'opzione se è necessario unire. Puoi creare un trigger che genera invece un errore.

Date un'occhiata a questo esempio: http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html#PLPGSQL-TRIGGER-EXAMPLE

CREATE TABLE emp (
    empname text, 
    salary integer, 
    last_date timestamp, 
    last_user text 
); 

CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$ 
    BEGIN 
     -- Check that empname and salary are given 
     IF NEW.empname IS NULL THEN 
      RAISE EXCEPTION 'empname cannot be null'; 
     END IF; 
     IF NEW.salary IS NULL THEN 
      RAISE EXCEPTION '% cannot have null salary', NEW.empname; 
     END IF; 

     -- Who works for us when she must pay for it? 
     IF NEW.salary < 0 THEN 
      RAISE EXCEPTION '% cannot have a negative salary', NEW.empname; 
     END IF; 

     -- Remember who changed the payroll when 
     NEW.last_date := current_timestamp; 
     NEW.last_user := current_user; 
     RETURN NEW; 
    END; 
$emp_stamp$ LANGUAGE plpgsql; 

CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp 
    FOR EACH ROW EXECUTE PROCEDURE emp_stamp(); 
0

... l'ho fatto così (nazwa = nome utente, firma = nome della società):

CREATE TABLE users 
(
    id bigserial CONSTRAINT firstkey PRIMARY KEY, 
    nazwa character varying(20), 
    firma character varying(50) 
); 


CREATE TABLE test 
(
    id bigserial CONSTRAINT firstkey PRIMARY KEY, 
    firma character varying(50), 
    towar character varying(20), 
    nazwisko character varying(20) 
); 

ALTER TABLE public.test ENABLE ROW LEVEL SECURITY; 

CREATE OR REPLACE FUNCTION whoIAM3() RETURNS varchar(50) as $$ 
declare 
    result varchar(50); 
    BEGIN 
select into result users.firma from users where users.nazwa = current_user; 
    return result; 
    END; 

    $$ LANGUAGE plpgsql; 


CREATE POLICY user_policy ON public.test 
    USING (firma = whoIAM3()); 

CREATE FUNCTION test_trigger_function() 
RETURNS trigger AS $$ 
BEGIN 
    NEW.firma:=whoIam3(); 
return NEW; 
END 
$$ LANGUAGE 'plpgsql' 
CREATE TRIGGER test_trigger_insert BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE test_trigger_function();