Коротко — 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 не получить.