2012-05-16 2 views
3

Я хочу использовать это для построения unique_key. Но я не хочу явно указывать имя схемы. Есть ли способ понять это?Определить имя схемы фактического запроса в функции

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) 
    RETURNS BOOLEAN AS 
$$ 
BEGIN 
    RETURN NOT EXISTS (
     SELECT header_id, value 
     FROM myschema.mytable 
     WHERE value LIKE _value AND header_id != _header_id 
     LIMIT 1); 
END; 
$$ LANGUAGE plpgsql; 

ALTER TABLE mytable 
    ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value)) 

Он должен работать, если я это сделать:

set search_path = schema1; 

CREATE FUNCTION AS ABOVE 

set search_path = schema2; 

INSERT INTO schema1.mytable(id, header_id, value) 
VALUES (1,1,'a'); -- Should be ok 

INSERT INTO schema1.mytable(id, header_id, value) 
VALUES (1,2,'a'); -- Should violate error 

Рассмотрим множественным схемы с такой же структурой таблицы.

Edt: 2012.05.16-1533 Рассмотрим эти таблицы, например: Header_table: идентификатор последовательного Snapshot_table: идентификатор последовательного, header_id Int (FK для заголовка таблицы), значение VARCHAR

id    id | header_id | value 
1    1 |   1 | a 
2    2 |   1 | a 
3    3 |   2 | b 
        4 |   3 | b 

и т.д. Запись с header_id 3 нарушило бы unique_key

Это относится к: Unique constraint on one column with excluding row with same values in other

EdT: 20120516-2356 Добавить некоторый тестовый сценарий (Sor чень для этого, я не знаю, как присоединиться файл)

drop schema if exists s1 cascade; 
drop schema if exists s2 cascade; 
drop schema if exists tp cascade; 

create schema s1; 
create schema s2; 
create schema tp; -- Temporary schema to hold insert function 

-- SCHEMA 1 -- 

set search_path = s1; 

-- * * * header table * * * 

CREATE TABLE th1 (
    id SERIAL NOT NULL, 
    constraint th1_pk_id PRIMARY KEY (id) 
) 
WITH (
    OIDS=FALSE 
); 

-- * * * snapshot table * * * 

CREATE TABLE ts1 (
    id SERIAL NOT NULL, 
    header_id INTEGER NOT NULL, 
    version INTEGER NOT NULL, 
    value VARCHAR NOT NULL, 
    CONSTRAINT ts1_pk_id PRIMARY KEY (id), 
    CONSTRAINT ts1_header_id_fk FOREIGN KEY (header_id) REFERENCES th1(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE 
) 
WITH (
    OIDS=FALSE 
); 

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) 
    RETURNS BOOLEAN AS 
$$ 
DECLARE 
    b boolean; 
BEGIN 
    RAISE INFO ' ---- CHECK UNIQUE FUNCTION ----'; 
    RAISE INFO 'Search_path: %', current_schema(); 
    RAISE INFO 'Function caled schema: %', 's1'; 
    SELECT NOT EXISTS (
     SELECT 1 
     FROM ts1 
     WHERE value LIKE _value AND header_id != _header_id 
     LIMIT 1) INTO b; 
    RAISE INFO 'Result: %', b; 
    RETURN b; 
END; 
$$ LANGUAGE plpgsql; 

ALTER TABLE ts1 
    ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value)); 

CREATE OR REPLACE FUNCTION logSchema() RETURNS TRIGGER AS $$ 
BEGIN 
    RAISE INFO 'Call on schema: %', 's1'; 
    RETURN new; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER logSchemaTrigger 
BEFORE INSERT OR UPDATE ON ts1 
FOR EACH ROW EXECUTE PROCEDURE logSchema(); 


-- SCHEMA2 -- 

set search_path = s2; 

-- * * * header table * * * 

CREATE TABLE th1 (
    id SERIAL NOT NULL, 
    constraint th1_pk_id PRIMARY KEY (id) 
) 
WITH (
    OIDS=FALSE 
); 

-- * * * snapshot table * * * 

CREATE TABLE ts1 (
    id SERIAL NOT NULL, 
    header_id INTEGER NOT NULL, 
    version INTEGER NOT NULL, 
    value VARCHAR NOT NULL, 
    CONSTRAINT ts1_pk_id PRIMARY KEY (id), 
    CONSTRAINT ts1_header_id_fk FOREIGN KEY (header_id) REFERENCES th1(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE 
) 
WITH (
    OIDS=FALSE 
); 

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) 
    RETURNS BOOLEAN AS 
$$ 
DECLARE 
    b boolean; 
BEGIN 
    RAISE INFO ' ---- CHECK UNIQUE FUNCTION ----'; 
    RAISE INFO 'Search_path: %', current_schema(); 
    RAISE INFO 'Function caled schema: %', 's2'; 
    SELECT NOT EXISTS (
     SELECT 1 
     FROM ts1 
     WHERE value LIKE _value AND header_id != _header_id 
     LIMIT 1) INTO b; 
    RAISE INFO 'Result: %', b; 
    RETURN b; 
END; 
$$ LANGUAGE plpgsql; 

ALTER TABLE ts1 
    ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value)); 

CREATE OR REPLACE FUNCTION logSchema() RETURNS TRIGGER AS $$ 
BEGIN 
    RAISE INFO 'Actual schema: %', 's2'; 
    RETURN new; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER logSchemaTrigger 
BEFORE INSERT OR UPDATE ON ts1 
FOR EACH ROW EXECUTE PROCEDURE logSchema(); 

-- INSERT PREPARE DATA -- 

-- insert function to handle error 
CREATE OR REPLACE FUNCTION tp.insert_data_test(_schemaname name, _header_id integer, _version integer, _value varchar) 
    RETURNS VOID AS 
$$ 
DECLARE 
    schemaname name; 
    b boolean; 
BEGIN 
    RAISE INFO '==== NEW INSERT COMMAND ===='; 
    SELECT _schemaname into schemaname; 
    IF schemaname IS NULL THEN 
     SELECT current_schema() INTO schemaname; 
    end if; 

    RAISE INFO 'Search_path: %', schemaname; 
    RAISE INFO 'Inserting data: %, %, %', _header_id, _version, _value; 

    BEGIN 
     EXECUTE 'SELECT NOT EXISTS (SELECT 1 FROM ' ||(select case when schemaname is null then 'null' else schemaname end)|| '.th1 WHERE id = ' || quote_literal(_header_id) || ' LIMIT 1)' INTO b; 
     IF (SELECT b) THEN  
      EXECUTE 'INSERT INTO ' || (select case when schemaname is null then 'null' else schemaname end) || '.th1 VALUES(' || quote_literal(_header_id) ||')'; 
     END IF; 

     EXECUTE 'INSERT INTO ' || (select case when schemaname is null then 'null' else schemaname end) || '.ts1(header_id, version, value) VALUES(' || quote_literal(_header_id) || ',' || quote_literal(_version) ||','|| quote_literal(_value) ||')'; 
    EXCEPTION WHEN others THEN 
     RAISE INFO 'Insert error: % %', SQLERRM, SQLSTATE; 
     RETURN; 
    END; 

    RAISE INFO 'Insert succesfull'; 
END; 
$$ LANGUAGE plpgsql; 

-- Wiht no current schema -- 
set search_path = default; 

SELECT tp.insert_data_test('s1',1,0,'a'); 
SELECT tp.insert_data_test('s2',1,0,'b'); 

/* IF you do this above with before set search_path its go fine, but if you do insert from newone query thats drop: 
ERROR: relation "ts1" does not exist 
LINE 3:  FROM ts1 
       ^
QUERY: SELECT NOT EXISTS (
     SELECT 1 
     FROM ts1 
     WHERE value LIKE _value AND header_id != _header_id 
     LIMIT 1) 
CONTEXT: PL/pgSQL function "is_value_free" line 3 at RETURN 

********** Chyba ********** 

ERROR: relation "ts1" does not exist 
Stav SQL: 42P01 
Kontext:PL/pgSQL function "is_value_free" line 3 at RETURN 
*/ 

-- Insert data with corect schema -- 
set search_path = s1; 
SELECT tp.insert_data_test(null,1,1,'a2'); 

set search_path = s2; 
SELECT tp.insert_data_test(null,1,1,'b2'); 

-- Insert data with incorect schema -- 
set search_path = s2; 
SELECT tp.insert_data_test('s1',1,2,'a3'); 

set search_path = s1; 
SELECT tp.insert_data_test('s2',1,2,'b3'); 

-- TEST DATA (Should for all violate unique_key)-- 
-- Wiht no current schema -- 
set search_path = default; 

SELECT tp.insert_data_test('s1',2,0,'a'); 
SELECT tp.insert_data_test('s2',2,0,'b'); 

-- Wiht corect current schema -- 
set search_path = s1; 
SELECT tp.insert_data_test(null,3,0,'a'); 

set search_path = s2; 
SELECT tp.insert_data_test(null,3,0,'b'); 

-- Wiht incorect current schema -- 
set search_path = s2; 
SELECT tp.insert_data_test('s1',4,0,'a'); 

set search_path = s1; 
SELECT tp.insert_data_test('s2',4,0,'b'); 

drop schema if exists s1 cascade; 
drop schema if exists s2 cascade; 
drop schema if exists tp cascade; 

EDT: 2012.05.28: 1937 Я трей его и нашел в базе данных (я не видел до того)

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) 
    RETURNS BOOLEAN AS 
    $BODY$ 
    BEGIN 
     RETURN SELECT NOT EXISTS 
      (
      SELECT NULL 
      FROM mytable 
      WHERE value LIKE $2 
        AND header_id != $1 
      ); 
    END; 
    $BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 

    ALTER FUNCTION schema1.is_value_free(integer, character varying) SET search_path=schema1, public; 

Но мне нужно иметь тот же код при создании функции базы данных и получить определение из базы данных обратно с функцией Postgre (например, резервное копирование ...)

+0

Почему вы используете 'оператор LIKE'? Как вы его используете, это то же самое, что и '=', если вы не включаете метасимволы '%' и '_'. –

+0

В моем случае дубликаты записей, если точное совпадение (не чувствительны к регистру). 'A1'! = 'A1' – Perlos

ответ

4
CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) 
    RETURNS BOOLEAN AS 
$$ 
     SELECT NOT EXISTS 
       (
       SELECT NULL 
       FROM mytable 
       WHERE value LIKE $2 
         AND header_id != $1 
       ); 
$$ LANGUAGE sql 
SET search_path FROM CURRENT; 

для того EXISTS полусоединение LIMIT 1 не требуется, а предметы SELECT не имеют значения.

Обратите внимание, что ваша уникальная функция не является коммутативной, поэтому содержимое таблицы будет зависеть от порядка внесения записей.

Update:

DROP SCHEMA IF EXISTS s1 CASCADE; 
DROP SCHEMA IF EXISTS s2 CASCADE; 
CREATE SCHEMA s1; 
CREATE SCHEMA s2; 

SET search_path = s1; 

DROP TABLE IF EXISTS mytable; 

CREATE TABLE mytable (id bigserial primary key, header_id integer not null, value text); 

CREATE OR REPLACE FUNCTION is_value_free(_header_id integer, _value varchar) 
    RETURNS BOOLEAN AS 
$$ 
     SELECT NOT EXISTS 
       (
       SELECT NULL 
       FROM mytable 
       WHERE value LIKE $2 
         AND header_id != $1 
       ); 
$$ LANGUAGE sql 
-- Below is the most important clause 
SET search_path FROM CURRENT; 

ALTER TABLE mytable 
    ADD CONSTRAINT uniq_value CHECK (is_value_free(header_id,value)); 

SET search_path = s2; 

DELETE 
FROM s1.mytable; 

INSERT INTO s1.mytable(id, header_id, value) 
VALUES (1,1,'a'); -- Should be ok 

INSERT INTO s1.mytable(id, header_id, value) 
VALUES (2,2,'a'); -- Should violate error 
+1

'FROM CURRENT' - очень приятная функция, но я подозреваю, что это не * то, что хочет OP. Он устанавливает 'search_path' функции в текущий путь search_path. Но OP, похоже, хочет, чтобы функция использовала схему, в которой находится таблица. Вышеприведенный пример потерпит неудачу - если я пойму вопрос после прочтения много раз. –

+1

@ErwinBrandstetter: Я считаю, что OP хочет писать сценарии для создания таблиц и ограничений для разных схем, только изменяя 'search_path'. Если этот способ создан, ограничение на 's1.mytable' будет использовать' s1.is_value_free', которое, в свою очередь, всегда будет проверять 's1.mytable', даже если вызывается с' search_path = s2'. – Quassnoi

+0

После повторного чтения я вижу вашу интерпретацию. У меня создалось впечатление, что OP пытается написать * one * функцию и повторно использовать ее много раз. Поскольку «FROM CURRENT» связывается во время создания, это не сработает. –