LINUX.ORG.RU

Странная ошибка при создании функции в PostgreSQL

 , , , транзакция,


0

2

Имеется следующий короткий код на Python. В основе кода используется библиотека psycopg2. Весь код работает в одной транзакции.

Что делает код? Он должен просто создать функцию с именем update_change_time_column(). После создания, функция, естественно, существует одна на всю схему.

# Рекомендательная блокировка на момент создания функции
self.exec( "SELECT pg_advisory_lock(12345);" )

# Проверка что функция уже есть в БД
resultCursor = self.exec( """SELECT EXISTS (
                          SELECT 1
                          FROM information_schema.routines
                          WHERE routine_name = 'update_change_time_column'
                            AND routine_schema = current_schema()
                          );""" )
isFunctionExists = resultCursor.fetchone()[0]

# Если функции нет, она создается
if not isFunctionExists:
    self.exec( """
               CREATE OR REPLACE FUNCTION update_change_time_column()
               RETURNS TRIGGER AS $$
               BEGIN
                 IF current_setting('custom.disable_change_time_trigger', true) IS NOT NULL THEN
                   RETURN NEW;
                 END IF;

                 NEW.change_time = now();
                 RETURN NEW;
               END;
               $$ language 'plpgsql';
               """ )

# Рекомендательная блокировка снимается
self.exec( "SELECT pg_advisory_unlock(12345);" )

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

И при работе иногда возникает ошибка:
Ошибка при выполнении SQL-запроса
CREATE OR REPLACE FUNCTION update_change_time_column()
RETURNS TRIGGER AS $$
BEGIN
  IF current_setting('custom.disable_change_time_trigger', true) IS NOT NULL THEN
    RETURN NEW;
  END IF;

  NEW.change_time = now();
  RETURN NEW;
END;

ОШИБКА:  повторяющееся значение ключа нарушает ограничение уникальности "pg_proc_proname_args_nsp_index"

DETAIL:  Ключ "(proname, proargtypes, pronamespace)=(update_change_time_column, , 2200)" уже существует.

Вопрос: почему возникает такая ошибка?

Вроде как предприняты меры по блокировке на время создания функции. Функция создается только в случае, если ее нет. Все обернуто в транзакцию. Что еще надо учитывать чтобы ошибки не возникало?

★★★★★

Ответ на: комментарий от gruy

https://www.psycopg.org/docs/connection.html#connection.set_isolation_level

https://www.psycopg.org/docs/extensions.html#psycopg2.extensions.ISOLATION_LE...

A new transaction is started at the first execute() command, but the isolation level is not explicitly selected by Psycopg: the server will use whatever level is defined in its configuration or by statements executed within the session outside Pyscopg control. If you want to know what the value is you can use a query such as show transaction_isolation.


То есть, psycopg2 по дефолту не контролирует уровень изоляции.

Команда «show transaction_isolation» показывает «read committed».

Xintrea ★★★★★
() автор топика

я ничего не понял, честно говоря с таким не сталкивался. но вообще думается, 1) что адвайзори лок тут не нужен, ddl транзакционный и на функции это тоже распространяется. 2) непонятно, почему «OR REPLACE» если ты проверяешь руками, непонятно, ты хочешь отказаться от создания если есть или заменить если есть 3) если ты проверяешь руками то делай это в одной транзакции, в общем случае используй контекcт-менеджер (вpsycopg2 with conn, conn.cursor() as cur ) 4) если ты проверяешь руками, нужно переключиться на RR (у тебя сецчас RC скорее всего) и быть готовым к тому, что при конфликте упадет (нужно проверять с чем, вероятно с 40001), то есть или это устраивает (ошибка ну и ладно) или ты организуешь цикл по 40001 на n попыток, возможно в этом случае от контекст-менеджера нужно будет отказаться в пользу try-except. ну то есть я бы сначала переделывал нормальным способом и смотрел бы будет ли эта ошибка

asdpm
()

и еще в приведенном коде нет коммит стейтмента, как оно работает вообще, не переведено ли psycopg в автокомит режим

asdpm
()

выполни print(dbconn.autocommit), получишь скорее всего true, нужно выключать autocommit, делать нормальные транзакции, тогда подобных чудес не должно происходить.

vtVitus ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.