PostgreSQL: Как передать строку в функцию без ошибки 'unknown'
Решение ошибки 'function does not exist' при передаче строкового литерала в PostgreSQL. Явное приведение типов ::text, ::varchar и CAST().
Как правильно передать строковый литерал (например, '2') в функцию PostgreSQL, чтобы избежать ошибки ‘function does not exist’ из-за определения типа параметра как unknown? Каков рекомендуемый способ явного указания типа varchar или text для таких аргументов?
Чтобы избежать ошибки ‘function does not exist’ при передаче строкового литерала в PostgreSQL, явно указывайте тип аргумента — например ::text, ::varchar или CAST(... AS text). По умолчанию строковый литерал получает псевдотип unknown, и при разрешении перегруженных функций это может привести к несоответствию сигнатур; приведение к text или к тому типу, который ожидает функция, решает проблему.
Содержание
- Как PostgreSQL интерпретирует строковые литералы (unknown)
- Почему возникает ошибка “function does not exist”
- Явное приведение: ::text, ::varchar и CAST() для функций PostgreSQL
- Примеры: crosstab, to_tsvector и пользовательские функции
- Практические советы и альтернативы (подготовленные запросы, параметры)
- Часто задаваемые вопросы (FAQ)
- Источники
- Заключение
Как PostgreSQL интерпретирует строковые литералы (тип unknown)
Строковый литерал в SQL-запросе без явного типа получает псевдотип unknown. Это семейство литералов — например '2', 'SELECT ...' — не связано автоматически с конкретным типом text или varchar до тех пор, пока система не решит, к какому типу привести значение. Раздел про приведение типов и разрешение функций в официальной документации описывает поведение псевдотипа unknown и порядок подбора перегрузок функций: PostgreSQL — Type conversion and functions.
Если аргумент функции остаётся unknown на этапе поиска нужной сигнатуры, система может не найти подходящую версию функции и вернуть ошибку. Поэтому часто достаточно явно указать тип литерала — это дает детерминированный выбор нужной перегрузки.
Почему возникает ошибка “function does not exist”
Алгоритм поиска функции сначала пытается найти точное совпадение типов аргументов. Если литерал имеет тип unknown, точного совпадения может не быть, и PostgreSQL не всегда автоматически выбирает желаемый тип. На практике это даёт ошибки вида:
ERROR: function myfunc(unknown) does not exist
LINE 1: SELECT myfunc('2');
^
Такая ошибка часто встречается при вызове функций из расширений или при перегрузках, где разные версии функции ожидают разные типы (например, text, varchar, regconfig и т.д.). Обсуждение похожих ситуаций есть в практических примерах сообщества, например в разборе ошибки для crosstab: ru.SO: ОШИБКА: функция crosstab(unknown) не существует.
Явное приведение: ::text, ::varchar и CAST() для функций PostgreSQL
Самые простые и распространённые способы явно указать тип строкового литерала:
-
Синтаксис PostgreSQL с двоеточием:
'строка'::text'строка'::varchar
-
Стандартный SQL:
CAST('строка' AS text)CAST('строка' AS varchar)
-
Конструктор типа (короткая форма):
text 'строка'— эквивалентноCAST('строка' AS text).
Примеры:
SELECT myfunc('2'::text);
SELECT myfunc(CAST('2' AS varchar));
-- или короткая форма
SELECT myfunc(text '2');
Для выбора версии функции с определённым типом аргумента явное приведение гарантирует, что PostgreSQL выберет нужную перегрузку. Документация PostgreSQL и русскоязычные разъяснения подробно описывают эти механизмы: PostgresPro — приведение типов и функции.
Замечание о text vs varchar: в PostgreSQL поля text и varchar бинарно совместимы, поэтому приведение между ними обычно не влечёт копирования данных; часто удобнее приводить к text, если функция принимает строку общего назначения.
Примеры: crosstab, to_tsvector и пользовательские функции
- crosstab (tablefunc). Ошибка часто возникает при передаче SQL-запроса как литерала:
-- Ошибка
SELECT * FROM crosstab('SELECT id, attr, value FROM source');
-- Правильно: явно указать тип text
SELECT * FROM crosstab('SELECT id, attr, value FROM source'::text);
Обсуждение конкретной ситуации: ru.SO: crosstab(unknown) не существует.
- to_tsvector (полнотекстовый поиск). Первый аргумент — конфигурация (
regconfig), второй — текст. Частая ошибка — передать литералы без приведения:
-- Может вызвать ошибку
SELECT to_tsvector('english', 'hello world');
-- Корректно, с явными типами
SELECT to_tsvector('english'::regconfig, 'hello world'::text);
Пример обсуждаемого случая: StackOverflow: ERROR: function to_tsvector(character varying, unknown) does not exist.
- Пользовательская перегруженная функция. Допустим есть версии:
CREATE FUNCTION f(text) RETURNS text AS $$ SELECT $1 || '!'; $$ LANGUAGE SQL;
CREATE FUNCTION f(int) RETURNS text AS $$ SELECT $1::text || '!'; $$ LANGUAGE SQL;
Вызов без приведения может быть неоднозначным:
SELECT f('2'); -- может не выбрать нужную перегрузку
SELECT f('2'::text); -- явно вызовет версию f(text)
SELECT f(CAST('2' AS int)); -- явно вызвать f(int) (если '2' число)
Если вы работаете со сложными типами (например, regclass, regconfig, jsonb), приведение к точному типу особенно желательно.
Практические советы и альтернативы (подготовленные запросы, параметры)
- Используйте приведение типа (
::text,CAST) для литералов в SQL, когда вызываете функции с перегрузками или расширениями. - Если вы формируете запросы из приложения — используйте параметризацию вместо конкатенации строк. Параметризованные запросы передают тип параметра из клиентского драйвера, что избавляет от проблем с
unknown.- Пример PREPARE/EXECUTE в psql:
PREPARE p(text) AS SELECT myfunc($1);
EXECUTE p('2'); -- $1 будет иметь тип text
- Если функция ожидает специфичный тип (например,
regconfig), приводите литерал именно к нему:'english'::regconfig. - Приведение
::textобычно безопасно и универсально;textиvarcharсовместимы, но при приведения кvarchar(n)следите за ограничением длины. - Для диагностики используйтe psql-утилиты:
\df funcname— показывают сигнатуры доступных функций, чтобы понять, к какому типу приводить литерал.
- Не избегайте приведения “чтобы работало” — лучше указать точный тип и быть уверенным, какую версию функции вы вызываете.
Часто задаваемые вопросы (FAQ)
- Можно ли не приводить литерал и рассчитывать на автоматическое приведение?
- Иногда PostgreSQL выполняет неявное приведение, но для разрешения перегрузок это ненадёжно — лучше привести явно.
- Чем
textлучшеvarcharдля приведения литералов?textне ограничен по длине и чаще используется как универсальный строковый тип; приведение кtextобычно достаточное и совместимо сvarchar.
- Как узнать, какие типы ожидает функция?
- В psql выполните
\df имя_функцииили посмотрите определение функции в каталогеpg_proc.
- В psql выполните
Источники
- Документация PostgreSQL — Type conversion and functions: https://www.postgresql.org/docs/current/typeconv-func.html
- Русская документация / пояснения (PostgresPro): https://postgrespro.ru/docs/postgresql/current/typeconv-func
- Обсуждение ошибки crosstab на ru.SO: https://ru.stackoverflow.com/questions/852445/ОШИБКА-функция-crosstabunknown-не-существует
- Обсуждение ошибки to_tsvector на StackOverflow: https://stackoverflow.com/questions/14523624/postgresql-error-function-to-tsvectorcharacter-varying-unknown-does-not-exis
Заключение
Коротко: строковый литерал в PostgreSQL имеет тип unknown, и при вызове функции лучше явно указать тип — ::text, ::varchar или CAST(... AS type). Чаще всего достаточно ::text; если функция ожидает специфичный тип (например, regconfig), приводите именно к нему. Это гарантирует, что PostgreSQL выберет правильную версию функции и вы избежите ошибки “function does not exist”.