Postgresql игнорирует условие if

#postgresql #stored-procedures #plpgsql #psql

Вопрос:

Я пытаюсь понять транзакции в plpgsql, и мне хотелось бы получить некоторые объяснения.

У меня есть этот код:

 CREATE OR REPLACE PROCEDURE MaJ(mode IN INT)
AS
$
DECLARE
r RECORD;
DECLARE
r RECORD;
BEGIN
    FOR r IN SELECT id, fname, lname, bday FROM usr
        LOOP
            IF r.ID % 2 = 0 THEN
                UPDATE usr SET lname = 'KONAN';
                RAISE NOTICE E'fname : %n', r.lname;
            END IF;
        END LOOP;
    IF mode = 0 THEN
        COMMIT;
    ELSE IF mode = 1 THEN
        ROLLBACK;
    END IF;
END;
 
  • во-первых, и после получения и опробования всех возможных решений, каков наилучший подход для достижения фиксации/отката на основе параметра процедуры.
  • во — вторых, помимо проблемы отката comit, postgres игнорирует if IF r.ID % 2 = 0 THEN все вместе и обновляет все записи, спасибо за любые объяснения.

Я вызываю эту процедуру из другой процедуры, используя CALL MaJ(VAL);

Обновление: Возможно, этот код лучше описывает проблему hte: Вот что я пытаюсь сделать:

 CREATE OR REPLACE PROCEDURE CRDM(Crtrn INOUT INT)
AS
$CRDM$
DECLARE
R RECORD;
BEGIN
    
        FOR R IN SELECT * FROM usr
            LOOP
                IF R.ID % 2 = 0 THEN
                    UPDATE USR SET lname = 'MAGNI' WHERE USR.ID = R.ID;
                END IF;
            END LOOP;
        IF Crtrn = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
END;
$CRDM$ LANGUAGE plpgsql;

CREATE OR REPLACE PROCEDURE AMI() 
AS
$AMI$
DECLARE
rtrn INT:=0;
BEGIN
    BEGIN
        CALL CRDM(rtrn);
    END;
END;
$AMI$ LANGUAGE plpgsql;

DO
$
BEGIN
    CALL AMI();
END;
$ LANGUAGE plpgsql;
 

Любая помощь в том, чего мне не хватает и как я могу лучше думать о транзакциях, приветствуется.

Комментарии:

1. Лучшее-это вопрос мнения. Но почему бы не использовать логическое значение для чего-то, что является логическим?

2. @jjanes Мне жаль, что этот язык вызывает у меня достаточно головной боли, не могли бы вы подробнее остановиться на своей точке зрения, спасибо

Ответ №1:

Возможность использования ROLLBACK или COMMIT только в новых версиях. Проверьте версию, которую вы используете, если поддерживаются эти команды.

Долгое время хранимые процедуры Postgres были без этих команд и без проблем. Использование COMMIT ROLLBACK внутри PL/pgSQL не слишком родное — в основном оно было реализовано для более легкой миграции из Oracle , но поскольку модели транзакций Oracle и Postgres сильно отличаются, существует множество ограничений.

Oracle запускает неявную транзакцию после входа в систему и ожидает явного commit или rollback . После выполнения этих команд Oracle немедленно запускает новую транзакцию.

В Postgres каждый оператор выполняется в рамках транзакции, как в Oracle. но если транзакция была запущена пользователем, то пользователь несет ответственность за commit или rollback . Если пользователь не запускал транзакцию, то Postgres запускает транзакцию, и Postgres неявно запускается rollback , когда операция вызывает исключение, или запускается commit , если нет. После того, как допускается только исключение rollback .

Обычно в Postgres нет необходимости использовать commit или rollback . Просто создайте исключение, и верхние слои выполнят всю необходимую работу.

Ваш код не очень хорош, пожалуйста, попробуйте прочитать документацию и некоторые примечания о стиле программирования. Не используйте верблюжью нотацию — в случае нечувствительного языка.

 CREATE TABLE foo_tab(id int);
CREATE TABLE boo_tab(id int, b text);

INSERT INTO foo_tab VALUES(1);
INSERT INTO foo_tab VALUES(2);

CREATE OR REPLACE PROCEDURE foo(a bool, b text)
AS $
DECLARE r record;
BEGIN
  FOR r IN SELECT * FROM foo_tab
  LOOP
    INSERT INTO boo_tab VALUES(r.id, b);
  END LOOP;
  IF a THEN
    COMMIT;
  ELSE
    ROLLBACK;
  END IF;
END
$ LANGUAGE plpgsql;

CREATE OR REPLACE PROCEDURE foo2(a bool, b text)
AS $
BEGIN
  CALL foo(a, b);
END;
$ LANGUAGE plpgsql;

DO $
BEGIN
  CALL foo2(true, 'Ahoj');
  CALL foo2(false, 'Nazdar');
END;
$; -- LANGUAGE plpgsql is implicit here

postgres=# SELECT * FROM boo_tab;
┌────┬──────┐
│ id │  b   │
╞════╪══════╡
│  1 │ Ahoj │
│  2 │ Ahoj │
└────┴──────┘
(2 rows)
 

Протестировано на Postgres 13

Комментарии:

1. Извините, но это не сработало, это выдает ошибку ERROR: invalid transaction termination CONTEXT: PL/pgSQL function foo(boolean,text) line 9 at COMMIT SQL statement "CALL foo(a, b)" PL/pgSQL function foo2(boolean,text) line 3 at CALL SQL statement "CALL foo2(true, 'Ahoj')" PL/pgSQL function inline_code_block line 3 at CALL SQL state: 2D000 ,я пытаюсь понять, какое это имеет отношение к включению и выключению автоматической фиксации, которую я пробовал в обоих случаях и все равно получаю ту же ошибку

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

3. @Majd — возможно, вы (или ваш клиент) уже начали транзакцию. Вы можете использовать ФИКСАЦИЮ, ОТКАТ только тогда, когда оператор ВЫЗОВА вызывается вне транзакции или подтранзакции. Myabe вы отключили автоматическую фиксацию в psql — это означает, что psql запустил транзакцию в фоновом режиме, и в этом случае вы не можете использовать подтранзакцию. Если вы используете psql, то вам необходимо включить автоматическую фиксацию в psql.

4. Это работает сейчас, я выполнял все сразу, после выполнения каждой процедуры перед выполнением анонимного блока это работало, только с Autocommit on тем, что нормально, какие-нибудь советы о том, как справиться с ситуацией, если меня попросят отключить автоматическую фиксацию?

5. @Majd — отключенная автоматическая фиксация — это просто функция psql-она не на стороне сервера, так что это не проблема для обычного приложения. Нет никакой возможности, как он может работать с отключенной автоматической фиксацией — этот случай просто не поддерживается. COMMIT или ROLLBACK может использоваться только тогда, когда транзакция запускается неявно — это предел реализации, и вы должны согласиться, нравится вам это или нет.

Ответ №2:

Ваша логика «ФИКСАЦИИ» и «ОТКАТА» выглядит для меня нормально после прочтения этого: https://www.postgresql.org/docs/current/plpgsql-transactions.html

Возможно, вам потребуется «НАЧАТЬ» транзакцию, прежде чем вы «ВЫЗОВЕТЕ» свою процедуру: я не уверен.

Ваша 2-я проблема проста: в вашем заявлении об ОБНОВЛЕНИИ нет предложения WHERE. Убедитесь, что вы обновили только текущую запись цикла, сопоставив значение идентификатора.

Комментарии:

1. вторая проблема решена сейчас, как и вы, однако первая проблема все еще существует, и это разочаровывает. Спасибо

2. @majd В чем первая проблема? Вы не описывали проблему, вы просто спрашивали мнение.

3. @jjanes откат фиксации не работает, и НАЧАЛО ; также не работает