2013-11-07 3 views
0

Я написал следующий триггер, чтобы гарантировать, что поле «fileequence» на вставке всегда имеет максимальное значение + 1 для одной заинтересованной стороны.Проблема параллелизма Postgres

CREATE OR REPLACE FUNCTION update_filesequence() 
     RETURNS TRIGGER AS ' 
    DECLARE 
    lastSequence file.filesequence%TYPE; 

    BEGIN 
    IF (NEW.filesequence IS NULL) THEN 

     PERFORM ''SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE''; 

     SELECT max(filesequence) INTO lastSequence FROM file WHERE stakeholder = NEW.stakeholder; 

     IF (lastSequence IS NULL) THEN 
     lastSequence = 0; 
     END IF; 

     lastSequence = lastSequence + 1; 

     NEW.filesequence = lastSequence; 
    END IF; 
    RETURN NEW; 
    END; 
' LANGUAGE 'plpgsql'; 

CREATE TRIGGER file_update_filesequence BEFORE INSERT 
    ON file FOR EACH ROW EXECUTE PROCEDURE 
    update_filesequence(); 

Но я повторил «filesequence» в базе данных:

select id, filesequence, stakeholder from file where stakeholder=5273; 
id  filesequence stakeholder 
6773 5    5273 
6774 5    5273 

По моему undertanding, в SELECT ... FOR UPDATE запирали две сделки на той же заинтересованной стороной, а затем второй один будет читать новое «fileequence». Но это не работает.

Я сделал несколько тестов на PgAdmin, выполнив следующие действия:

BEGIN; 
select id from stakeholder where id = 5273 FOR UPDATE; 

И действительно LOCKED других записей вставляются в ту же заинтересованную сторону. Тогда кажется, что LOCK работает.

Но когда я запускаю приложение с одновременными загрузками, я вижу, что повторяю.

Кто-то может помочь мне найти, что является проблемой с моим триггером?

Thanks, Douglas.

+0

В plpgsql, не первый BEGIN отмечает начало блока, а не начало транзакции? –

+0

Личный вопрос: где вы научились использовать 'PERFORM', как это? Я видел много людей, которые это делали, и я не знаю, почему. (это неправильно, см. мой ответ) ... – MatheusOl

+0

@ MikeSherrill'Catcall ', что 'BEGIN' не запускает транзакцию, но на PostgreSQL функция ** всегда ** называется ** внутри транзакции **. – MatheusOl

ответ

1

Ваша идея правильная. Чтобы получить автоинкремент на основе другого поля (предположим, что он назначен группе), вы не можете использовать последовательность, тогда вам нужно заблокировать строки этой группы, прежде чем увеличивать ее.

Логика вашей функции запуска делает это. Но вы неправильно поняли об операции PERFORM. Он должен быть помещен вместо ключевого слова SELECT, поэтому он не получает строку в качестве параметра. Это означает, что когда вы делаете:

PERFORM 'SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE'; 

ФЛ/PGSQL фактически выполняется:

SELECT 'SELECT id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE'; 

И не обращая внимания на результат.

То, что вы должны делать на этой линии:

PERFORM id FROM stakeholder WHERE id = NEW.stakeholder FOR UPDATE; 

То есть, измените только эту линию, и вы сделали.

+0

Работал отлично. Большое спасибо! – DHansen

Смежные вопросы