Основи безпеки
Supabase PGRST202: "Could not find the function in the schema cache"
Supabase "Could not find the function in the schema cache" показує назви аргументів, які надіслав ваш застосунок. Порівняйте їх із функцією.

Коротко
- Supabase "Could not find the function in the schema cache" (PGRST202) означає, що жодна функція не має такої назви й не приймає саме ті назви аргументів, які надіслав ваш застосунок. Назви в дужках у повідомленні належать тому, що надіслав ваш застосунок.
- Спершу перевірте назви аргументів: userId, надісланий у функцію, яка оголошує user_id, пропущений префікс p_ або значення, яке було undefined і тому так і не надійшло.
- Supabase сам оновлює свій список функцій щоразу, коли функцію створюють, змінюють або видаляють, тож перевірте назви та схему, перш ніж щось перезавантажувати.
Ваш білдер написав функцію бази даних, яка завантажує замовлення клієнта, а сторінка, що її викликає, лишається порожньою. Консоль браузера показує ось це:
{
"code": "PGRST202",
"details": "Searched for the function public.get_orders with parameter userId or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.",
"hint": "Perhaps you meant to call the function public.get_orders(user_id)",
"message": "Could not find the function public.get_orders(userId) in the schema cache"
}
Supabase "Could not find the function in the schema cache" означає, що в PostgREST
немає функції з такою назвою, яка приймає саме ті аргументи, що надіслав ваш
застосунок. PostgREST є частиною Supabase, яка відповідає на виклики
supabase.rpc() вашого застосунку, і він веде власний список усіх функцій, які
може викликати. Цей список називається schema cache.
Ось що більшість відповідей подає неправильно: вони радять перезавантажити
schema cache. У Supabase цей список оновлюється сам щоразу, коли функцію
створюють або змінюють. Повідомлення вже сказало вам, що не так. userId у дужках
надіслав ваш застосунок. user_id у підказці очікує функція.
Що означає "Could not find the function in the schema cache"?
Жодна функція в схемі, де шукав PostgREST, не має такої назви разом саме з такими назвами аргументів.
Уявіть кожну функцію бази даних як надрукований бланк, приколотий на стіні за
віконцем: угорі її назва, а нижче підписане поле для кожного аргументу. Коли ваш
застосунок викликає supabase.rpc('get_orders', { userId: id }), він подає
заповнену копію. PostgREST шукає на стіні бланк із такою самою назвою й такими
самими підписами полів. Якщо такого немає, копію розвертають біля віконця, а
повідомлення зачитує вам підписи, які ви заповнили.
Чотири речі зафіксовані у власному коді PostgREST:
- код
PGRST202, а статус HTTP 404, той самий, що в сторінки, якої не існує - у дужках перелічено назви аргументів, які надіслав ваш застосунок. Якщо він не
надіслав жодного, замість них у повідомленні стоїть
without parameters public.перед назвою означає схему, у якій шукав PostgREST- підказка, якщо вона є, називає найближчу функцію, яку PostgREST знає, з її власними назвами аргументів
Насамперед порівняйте дужки з підказкою.
Причина 1: назви аргументів не збігаються
PostgREST вибирає функцію за її назвою та назвами аргументів разом, тож функція, яку викликали з неправильними назвами, для PostgREST просто не існує.
Правила, з пошуку в планувальнику PostgREST:
- кожен аргумент без значення за замовчуванням треба надіслати під його точною назвою
- аргумент зі значенням за замовчуванням можна пропустити
- назва, якої функція не оголошує, виключає цю функцію, навіть коли всі інші назви збігаються
Назва може бути неправильною трьома способами, які легко проґавити. Перший
виникає, коли JavaScript зустрічає SQL: ваш застосунок пише userId, а функція
оголошує user_id.
Другий стосується префікса. Деякі функції називають свої аргументи p_user_id,
щоб їх не сплутали зі стовпцем user_id, і тоді виклик теж мусить нести p_. В
одному
обговоренні Supabase виклик
надсилав id, nombre і ще три назви, функція оголошувала p_id, p_nombre і
решту, а підказка надрукувала всі правильні назви.
Третій пов'язаний із правилом, яке перейменовує й стовпці. Назву, написану в SQL
без подвійних лапок, переводять у нижній регістр, тож
create function get_orders(userId uuid) зберігає аргумент як userid.
Користувач FlutterFlow
натрапив саме на це: виклик надсилав userId, а підказка відповіла (userid).
Версія цієї помилки для стовпців має
таблицю того, що створює кожне написання.
Щоб побачити назви, які зберегла база, виконайте це в SQL Editor:
select p.proname, pg_get_function_arguments(p.oid)
from pg_proc p
join pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public' and p.proname = 'get_orders';
pg_get_function_arguments показує аргументи так, як їх оголосили, разом зі
значеннями за замовчуванням, тож видно й те, які з них можна пропустити.
Безпечніше виправлення робиться в застосунку, бо воно нічого не змінює в базі.
Попросіть білдер викликати функцію за збереженими назвами: "get_orders приймає
user_id; передавайте { user_id: … } у кожному виклику .rpc() до неї".
Перейменувати аргумент складніше, ніж здається.
Документація Postgres
каже про CREATE OR REPLACE FUNCTION: "You cannot change the name already
assigned to any input parameter". Функцію доведеться видалити й створити знову, і
кожен інший екран, який викликає її за старою назвою, після цього падає так само.
Причина 2: значення було undefined, тож назва так і не надійшла
Аргумент зі значенням undefined ніколи не доходить до PostgREST, і функцію тоді
шукають без нього.
supabase-js надсилає ваші аргументи як JSON, а в JSON немає undefined.
Довідка MDN про JSON.stringify
каже, що такі значення "are either omitted (when found in an object) or changed to
null (when found in an array)". Тож цей виклик, зроблений до того, як завантажився
користувач, що увійшов:
await supabase.rpc('get_orders', { user_id: user?.id })
надсилає {}, і відповідь буде
Could not find the function public.get_orders without parameters in the schema cache.
Якщо аргументів два й один із них undefined, у дужках буде лише той, що дійшов.
Назва у вашому коді правильна, тому читання коду цього не знаходить. Повідомлення знаходить: дужки порожні або в них на одну назву менше, ніж передає код.
Виправлення полягає в тому, щоб робити виклик, коли значення вже є. Якщо функція
вміє обробляти відсутнє значення, передавайте null навмисно, бо JSON його
зберігає.
Фільтр робить із тим самим значенням протилежне. .eq('user_id', user?.id)
вставляє його в запит словом undefined, і колонка uuid відповідає
invalid input syntax for type uuid.
Причина 3: функція в іншій схемі
supabase.rpc() шукає в схемі public, яку й називає повідомлення, якщо ви не
вкажете інше. Функції, створеної деінде, у списку PostgREST немає взагалі:
він завантажує функції
тільки зі схем, які ваш проєкт відкриває через свій API.
Це трапляється у двох формах:
- функцію створили у власній схемі, наприклад
privateчиbilling, а викликають простим.rpc('get_orders') - схему вписали в назву, як
.rpc('billing.get_orders'). PostgREST читає весь рядок як одну назву й шукає її вpublic, тож у повідомленні будеpublic.billing.get_orders
Функцію з іншої схеми викликають через .schema('billing').rpc('get_orders', …),
і ця схема має бути в списку Exposed schemas у налаштуваннях API проєкту, як
показує
посібник Supabase про власні схеми.
Доки її там немає, відповідь змінюється на PGRST106, Invalid schema: billing, з
підказкою, яка перелічує відкриті схеми.
Відкрита так, як це робить той посібник, схема відчиняє кожну свою таблицю.
Посібник дає anon і authenticated доступ до кожної таблиці нової схеми, і
відтоді лише ваші правила
Row Level Security вирішують, що
відвідувач може там прочитати.
Версія цієї помилки для таблиць має
запит, який показує, на яких таблицях схеми ці правила вимкнені, і його варто
виконати до відкриття схеми. Деякі функції тримають у приватній схемі саме для
того, щоб ніхто не міг викликати їх ззовні: наш
посібник про нескінченну рекурсію в policy
кладе одну туди саме з цієї причини. Виклик такої функції з браузера отримує
PGRST202 навмисно, і її схема має лишатися закритою.
Причина 4: назва самої функції, або функції немає взагалі
PostgREST шукає назву точно, літера в літеру й з урахуванням регістру.
- одруківка, або
getOrdersу виклику протиget_ordersу базі - функція, створена як
getOrdersбез подвійних лапок, яка зберігається якgetordersза тим самим правилом, що й її аргументи - процедура. PostgREST перелічує лише функції, тож
create procedureстворює щось, до чого.rpc()не дістане, і так само з функцією, щоreturns trigger: її запускає тригер, а ніколи не ваш застосунок
Підказка допомагає й тут. Коли функції з такою назвою немає, PostgREST пропонує найближчу назву, яку має, якщо якась достатньо схожа.
Або функцію так і не створили в базі, до якої звертається ваш застосунок. Якщо ви будуєте в Lovable, перегляньте чат у пошуках SQL, який вас просили схвалити. Документація Lovable про Supabase каже, що він "writes the SQL, shows it to you, and asks for your approval in the project chat before running it". Доки ви не схвалите, функція існує лише в коді, який її викликає. Якщо хтось створив її вручну, перевірте, що вона потрапила в той проєкт, з яким говорить ваш застосунок: гілка є окремим проєктом зі своєю базою.
Причина 5: кеш справді застарів
У Supabase це рідкісний випадок. Кожна база Supabase має event trigger
pgrst_ddl_watch, який надсилає NOTIFY pgrst, 'reload schema' після кожного
CREATE FUNCTION і ALTER FUNCTION, а другий, pgrst_drop_watch, надсилає те
саме після видалення функції. Обидва є в
налаштуванні бази від Supabase.
Коли це повідомлення не доходить, перезавантажте список вручну в SQL Editor:
NOTIFY pgrst, 'reload schema';
Якщо нічого не змінилося, версія цієї помилки для стовпців описує два наступні кроки: застряглу чергу сповіщень і перезапуск проєкту. Обидва працюють для функцій так само.
Коли дві функції мають одну назву
PostgREST не вгадує між ними. Коли дві функції мають однакову назву й обидві
приймають аргументи, які надіслав ваш застосунок, виклик падає з PGRST203 і
HTTP 300:
Could not choose the best candidate function between: public.get_orders(user_id => uuid), public.get_orders(user_id => text)
Виправлення PGRST202 буває одним зі шляхів сюди. Ваш білдер змінює тип аргументу
через CREATE OR REPLACE FUNCTION, і Postgres нічого не замінює. Його
документація
каже, що типи аргументів так змінити не можна: "if you tried, you would actually
be creating a new, distinct function". Тепер обидві версії існують поруч.
Підказка PostgREST для цього випадку звучить так: "Try renaming the parameters or the function itself in the database so function overloading can be resolved". Простіше виправлення полягає в тому, щоб видалити версію, яка вам не потрібна, назвавши тип її аргументу, щоб Postgres знав, про яку йдеться:
drop function public.get_orders(text);
Перевірте, яку версію викликає ваш застосунок, перш ніж видаляти будь-яку з них.
Як правильно викликати функцію Supabase
Передавайте один об'єкт, ключі якого збігаються з назвами аргументів функції в
тому написанні, яке зберегла база. У
посібнику Supabase про функції бази даних
функцію, оголошену як add_planet(name text), викликають так:
supabase.rpc('add_planet', { name: 'Jakku' }).
Щойно ваш застосунок може викликати функцію, це може зробити й будь-хто інший. Той
самий посібник каже прямо: "By default, any role can run a database function."
Сюди входить anon, роль за кожним запитом відвідувача, який не увійшов. Функцію,
яку ваш застосунок викликає з браузера, може викликати будь-хто з вашим публічним
ключем, із будь-якими аргументами на свій вибір.
Для функції, яку мають запускати лише користувачі, що увійшли, зробіть як у
посібнику: заберіть execute у public і anon та поверніть його
authenticated.
revoke execute on function public.get_orders from public, anon;
grant execute on function public.get_orders to authenticated;
Ваші таблиці становлять другу половину того, до чого дістає цей публічний ключ. Наше безкоштовне сканування перевіряє ззовні, чи віддають вони рядки будь-кому, хто має цей ключ, і не потребує облікового запису.
Зміна функції, від якої залежить ваш застосунок
Майте копію бази з моменту перед зміною й дізнавайтеся, коли змінюється щось, що відкриває ваш застосунок.
Reeve Care зберігає копію вашої бази даних Supabase:
- зашифровану копію за розкладом, у власному сховищі Reeve в ЄС, поза вашим акаунтом Supabase
- копія з моменту перед вашою зміною містить ваші функції такими, якими їх тоді визначили, поруч із таблицями й рядками
- кожну копію перевірено, перш ніж вона зарахується, підрахунком рядків у кожній таблиці
- також ваші завантажені файли, щойно ви підключите обліковий доступ до Storage
- відновлення в один клік, перед яким робиться страхова копія поточного стану
- усе, що робить Monitor
Reeve Monitor і далі перевіряє ззовні:
- чи ваш застосунок доступний, перевірка щохвилини
- повну перевірку безпеки щогодини, до трьох застосунків
- повідомлення, коли результат змінюється, тож деплой, який змінює те, що відкриває ваш застосунок, дійде до вас без того, щоб ви ходили дивитися
- щомісячний звіт простою мовою про те, що він бачив
Що робити зараз
Що робити
- Прочитайте повідомлення: назви в дужках надіслав ваш застосунок, а підказка називає те, що є в базі.
- Порівняйте назви аргументів літера в літеру, разом із префіксом
p_. Назва, написана в SQL без лапок, зберігається малими літерами. - Якщо дужки порожні або в них бракує назви, шукайте значення, яке під час виклику було
undefined. - Перевірте схему в повідомленні. Функції з іншої схеми потрібні
.schema('назва').rpc(…)і відкрита схема, а відкрита схема робить доступними й свої таблиці. - Якщо дві функції мають одну назву, видаліть версію, яку ваш застосунок не викликає.
- Запускайте
NOTIFY pgrst, 'reload schema';лише тоді, коли назви вже збігаються. - Заберіть
executeвanonдля кожної функції, яку відвідувач без входу не повинен запускати.
Почніть із дужок, бо в них саме те, що надіслав ваш застосунок. Перш ніж видаляти або замінювати функцію, від якої залежить ваш застосунок, збережіть копію бази з моменту перед зміною.
Поширені запитання
Що означає PGRST202?
PGRST202 це код, який повертає PostgREST, частина Supabase, що відповідає на виклики supabase.rpc(), коли жодна функція не підходить до виклику. Він приходить із HTTP 404 і повідомленням "Could not find the function public.name(arguments) in the schema cache". Назви в дужках означають аргументи, які надіслав ваш застосунок, а public означає схему, у якій ішов пошук.
Функція існує. Чому мій RPC-виклик каже, що її немає?
PostgREST розпізнає функцію за її назвою та назвами її аргументів разом. Надсилайте кожен аргумент без значення за замовчуванням саме під тією назвою, яку зберегла база, і нічого, чого функція не оголошує. Назва, написана в SQL без подвійних лапок, зберігається малими літерами, тож userId у create function стає userid.
Чому в помилці написано "without parameters"?
Ваш застосунок не надіслав жодного аргументу. З supabase-js звична причина в значенні, яке під час виклику було undefined: аргументи передаються як JSON, а JSON пропускає значення undefined, тож { user_id: undefined } приходить як {}. Робіть виклик, коли значення вже є, або передавайте null, якщо функція вміє обробляти відсутнє значення.
Чи має функція бути в схемі public?
supabase.rpc() шукає в public, якщо ви не вибрали іншу схему через .schema('назва'), і ця схема має бути в списку Exposed schemas у налаштуваннях API проєкту. Відкрита схема робить свій вміст доступним через API, тож це варто вирішувати свідомо.
Як перезавантажити schema cache у Supabase?
Виконайте NOTIFY pgrst, 'reload schema'; у SQL Editor. Supabase і так надсилає це повідомлення сам щоразу, коли функцію створюють, змінюють або видаляють, тож перезавантаження допомагає лише тоді, коли повідомлення не дійшло. Спершу перевірте назви.