Ошибка PostgreSQL 42P11: invalid_cursor_definition — отладка курсоров
- Ошибка
ERROR: invalid cursor definition (SQLSTATE 42P11)при объявлении курсора. - Конфликт опций при создании курсора (например, одновременное указание
SCROLLи несовместимых выраженийFOR UPDATE). - Сбои пакетной выгрузки данных через курсоры в интеграционных скриптах 1С.
1. Устранение несовместимости параметров SCROLL и FOR UPDATE
Курсоры, использующие блокировку строк FOR UPDATE / FOR SHARE, не могут быть объявлены как двунаправленные (SCROLL):
-- Ошибочное объявление:
DECLARE cur_docs SCROLL CURSOR FOR
SELECT * FROM _document100 FOR UPDATE; -- Ошибка 42P11
-- Исправленное объявление (только прямое чтение NO SCROLL):
DECLARE cur_docs NO SCROLL CURSOR FOR
SELECT * FROM _document100 FOR UPDATE;2. Использование курсоров, удерживаемых между транзакциями (WITH HOLD)
BEGIN;
DECLARE cur_export CURSOR WITH HOLD FOR
SELECT id, payload FROM export_queue WHERE processed = false;
COMMIT;
-- Теперь курсор доступен вне исходной транзакции:
FETCH NEXT FROM cur_export;
CLOSE cur_export;3. Корректная работа с курсорами в PL/pgSQL
CREATE OR REPLACE FUNCTION process_large_table()
RETURNS void AS $$
DECLARE
rec RECORD;
cur CURSOR FOR SELECT id, balance FROM accounts;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rec;
EXIT WHEN NOT FOUND;
-- Обработка записи
END LOOP;
CLOSE cur;
END;
$$ LANGUAGE plpgsql; Частые вопросы (FAQ)
Что означает опция WITH HOLD при объявлении курсора?
По умолчанию курсор закрывается автоматически при завершении текущей транзакции (COMMIT). Опция WITH HOLD позволяет сохранять курсор открытым для последующих транзакций в рамках той же сессии.
Каковы накладные расходы при использовании курсоров WITH HOLD?
В момент выполнения COMMIT ядро PostgreSQL материализует весь оставшийся результат запроса во временный дисковый файл tuplestore, что может вызвать всплеск дискового I/O.
Использует ли платформа 1С курсоры PostgreSQL?
1С:Предприятие использует порталы и курсоры для постраничной динамической выборки данных в динамических списках пользовательского интерфейса.
Как принудительно закрыть зависший курсор?
Выполните команду CLOSE <cursor_name>; либо выполните CLOSE ALL; для закрытия всех открытых курсоров текущей сессии.