Как при вызове хранимой процедуры получить сообщения, выведенные оператором RAISE NOTICE?

При вызове хранимой процедуры хочу получать отчёт о статусе выполнения в реальном времени — как перехватить NOTICE во время выполнения (а не только в конце)? <br/> <br/> Для запуска процедуры использую pyodbc.
  • 13 апреля 2026 г. 10:03
Ответы на вопрос 3
Коротко — pyodbc/ODBC в общем случае не даёт «стримить» RAISE NOTICE в реальном времени так, как это делает клиент на libpq (psql / psycopg2). У вас есть 3 рабочих пути:

1) Поменять драйвер на libpq-ориентированный (например psycopg2/psycopg) и использовать callback для NOTICE — тогда сообщения будут приходить по мере их получения клиентом.
2) Переписать логику процедуры так, чтобы она посылала события через NOTIFY (и клиент слушал LISTEN) или писала статус в таблицу, и клиент периодически опрашивал эти события/таблицу.
3) Оставаться на pyodbc, но понимать, что большинство ODBC‑драйверов возвращают диагностические сообщения только по завершении операции — реального стрима нет (возможны нюансы в конкретном драйвере psqlODBC, но pyodbc не предоставляет удобного notice‑hook).

Ниже — примеры и пояснения.

1) Лучший вариант: psycopg2 (реальное время)
- psycopg2 даёт возможность установить notice‑receiver, который вызывается, когда сервер шлёт NOTICE (через libpq). Пример:

import psycopg2

def notice_receiver(notice):
    # notice — объект/строка, содержит текст NOTICE
    print("NOTICE:", notice.message.strip())

conn = psycopg2.connect("dbname=... user=... password=...")
conn.set_notice_receiver(notice_receiver)

cur = conn.cursor()
cur.execute("CALL my_proc();")  # или SELECT my_func(); в зависимости от типа
# Пока выполняется процедура, RAISE NOTICE будут печататься notice_receiver

2) Вариант с NOTIFY / LISTEN
- Если вы можете модифицировать процедуру, она может отправлять pg_notify (PERFORM pg_notify('chan', payload)); клиент делает LISTEN и получает уведомления асинхронно.
- Важно: уведомления NOTIFY доставляются слушателям при фиксации транзакции (commit). Если процедура выполняется в одной транзакции и вы хотите промежуточные сообщения, нужно чтобы процедура могла делать COMMIT между шагами (т.е. это должна быть именно PROCEDURE, а не FUNCTION, и она должна управлять транзакциями), либо использовать альтернативу.

Пример клиента (psycopg2):

import select, psycopg2

conn = psycopg2.connect(...)
cur = conn.cursor()
cur.execute("LISTEN my_channel;")
conn.commit()

# ожидаем уведомлений
while True:
    if select.select([conn], [], [], 10) == ([], [], []):
        continue
    conn.poll()
    while conn.notifies:
        n = conn.notifies.pop(0)
        print("NOTIFY:", n.payload)

3) Если вы ограничены pyodbc
- pyodbc использует ODBC‑драйвер; многие драйверы возвращают NOTICE/INFO только после завершения вызова, и pyodbc не предоставляет hook для получения уведомлений «в процессе». Попробуйте посмотреть cursor.messages или connection.messages (в зависимости от версии pyodbc) — но чаще всего это накопленный список после выполнения запроса, а не поток.
- Практические альтернативы при использовании pyodbc:
  - переписать процедуру так, чтобы она писала прогресс в вспомогательную таблицу; клиент в отдельном потоке/цикле периодически опрашивает эту таблицу через pyodbc;
  - использовать NOTIFY/LISTEN и отдельное соединение через psycopg2/asyncpg для прослушки (т.е. оставить выполнение через pyodbc, а прослушивание через libpq‑клиент);
  - логировать прогресс в серверный лог и читать лог (неудобно).

Резюме
- Для «реального времени» при RAISE NOTICE — переключитесь на libpq‑драйвер (psycopg2/psycopg) и используйте notice_receiver, либо используйте NOTIFY/LISTEN (учитывая особенности доставки при транзакциях), либо применяйте запись прогресса в таблицу + поллинг. Если остаетесь на pyodbc — скорее всего точного стрима NOTICE не получить.
pyodbc нотисы из постгреса в норме не пробрасывает — ODBC-прослойка их теряет. Переходи на psycopg2. <br/> <br/> С ним всё просто: нотисы копятся в conn.notices уже после execute(). А если хочется ловить в реальном времени — переопредели список: <br/> <br/> <pre><code>class Log(list):
    def append(self, item):
        print(item.strip())
        super().append(item)

conn.notices = Log()
cur.execute("CALL my_proc()")</code></pre> <br/> <br/> Каждый RAISE NOTICE будет прилетать по мере буферизации, не ждать конца процедуры.
Используй библиотеку psycopg2 вместо pyodbc. Это "родной" драйвер для постгреса который превосходно работает с уведомлениями.
Похожие вопросы