Внутренности PostgreSQL: что такое Portal
24 июня 2024
Изучая исходный код PostgreSQL, можно повстречать сущность под названием Portal. Возникает закономерный вопрос — что это такое, а также где и для чего используется? Попробуем разобраться.
Чтобы вживую увидеть Portal, подключимся gdb к бэкенду PostgreSQL и поставим следующую точку останова:
(gdb) c
Если теперь выполнить запрос:
… то будет получен такой стэктрейс:
#0 DefineRelation
#1 0x000055559a5ebe88 in ProcessUtilitySlow
#2 0x000055559a5ebc80 in standard_ProcessUtility
#3 0x000055559a5eafa8 in ProcessUtility
#4 0x000055559a5e87b8 in PortalRunUtility
#5 0x000055559a5e8a44 in PortalRunMulti
#6 0x000055559a5e7eb8 in PortalRun (portal=0x5555c6a287b0, ... )
#7 0x000055559a5df9e4 in exec_simple_query
#8 0x000055559a5e4e04 in PostgresMain
#9 0x000055559a5db8b8 in BackendMain
#10 0x000055559a4ed70c in postmaster_child_launch
#11 0x000055559a4f4668 in BackendStartup
#12 0x000055559a4f0d30 in ServerLoop
#13 0x000055559a4f0598 in PostmasterMain
#14 0x000055559a3dc638 in main
Продолжая экспериментировать с разными запросами, мы убеждаемся, что их исполнение всегда происходит через PortalRun(), где portal
фигурирует в качестве аргумента. За дальнейшими разъяснениями обратимся к исходному коду в portalmem.c и portal.h.
Из кода мы узнаем, что же собой представляет портал. Portal — это структура, отражающая состояние исполнения запроса. Один Portal соответствует одному SQL-запросу и, соответственно, имеет один возвращаемый результат.
Портал реализован, как структура PortalData. В ней немало полей. Все их мы разбирать не буем, ограничившись лишь парой наиболее интересных:
Портал имеет выделенный ему MemoryContext. C MemoryContext’ами к этому моменту мы уже знакомы.
Также каждому порталу присваивается ResourceOwner. Это отдельная важная структура, однако ее обсуждение выходит за рамки поста. Если в двух словах, то ResourceOwner отвечает за своевременное освобождение ресурсов, таких как открытые файлы, pin’ы на разделяемые буферы, и так далее.
Хук, вызываемый при освобождении портала. Как правило, это PortalCleanup(), объявленный в portalcmds.c.
Текст SQL-запроса. В PostgreSQL ≥ 8.4 здесь не может быть NULL.
Параметры запроса.
Чтобы посмотреть, как эта структура заполняется, в psql выполним:
Затем создадим точку останова на PortalRun() и выполним запрос:
Точка останова срабатывает дважды:
#1 0x000055559a2cc158 in ExecuteQuery
#2 0x000055559a5eb5e0 in standard_ProcessUtility
#3 0x000055559a5eafa8 in ProcessUtility
#4 0x000055559a5e87b8 in PortalRunUtility
#5 0x000055559a5e84f8 in FillPortalStore
#6 0x000055559a5e7e2c in PortalRun <- первый раз
#7 0x000055559a5df9e4 in exec_simple_query
#8 0x000055559a5e4e04 in PostgresMain
#9 0x000055559a5db8b8 in BackendMain
#10 0x000055559a4ed70c in postmaster_child_launch
#11 0x000055559a4f4668 in BackendStartup
#12 0x000055559a4f0d30 in ServerLoop
#13 0x000055559a4f0598 in PostmasterMain
#14 0x000055559a3dc638 in main
Значение полей при первом вызове функции:
$1 = 0x5555c69b31c0 "EXECUTE foo(123);"
(gdb) p portal->portalParams
$2 = (ParamListInfo) 0x0
… и при втором:
$3 = 0x5555c6ac73f0 "PREPARE foo(int) AS SELECT $1;"
(gdb) p portal->portalParams->numParams
$4 = 1
(gdb) p portal->portalParams->params[0]
$5 = {value = 123, isnull = false, pflags = 1, ptype = 23}
Видим, как через portalParams
был передан аргумент для prepared statement.
Над порталами определен ряд функций, такие как CreatePortal(), PortalDrop(), PortalDefineQuery(), GetPortalByName() и другие. Полный список есть в portal.h, а датели реализации можно найти в portalmem.c. Заинтересованные читатели могут ознакомиться с этими функциями самостоятельно. Они простые.
Это базовые операции над порталами, а вся логика находится в файле pquery.c. Основных функций две — это PortalStart():
void
PortalStart(Portal portal, ParamListInfo params,
int eflags, Snapshot snapshot)
… и PortalRun():
bool
PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
DestReceiver *dest, DestReceiver *altdest,
QueryCompletion *qc)
В pquery.c есть комментарии относительно всех аргументов, возвращаемых значений, и т.д. Пример практического использования функций CreatePortal(), PortalDefineQuery(), PortalStart(), PortalRun() и PortalDrop() можно найти в функции exec_simple_query(), уже знакомой нам по представленным выше стектрейсам.
Конечно же, полная картина сложнее. Однако углубляться в изучение порталов еще сильнее как будто бы нет особого смысла. Даже разработчики PostgreSQL исключительно редко трогают порталы. В этом легко убедиться при помощи команды git log
. Обычно достаточно в общих чертах понимать, что они из себя представляют. Если же когда-нибудь вам придется с головой погрузиться в детали реализации, то теперь у вас есть отправная точка. Для получения самой полной и актуальной информации рекомендуется читать код и ставить эксперименты под отладчиком.
Дополнение: В продолжение темы порталов вас может заинтересовать статья Внутренности PostgreSQL: сетевой протокол.
Метки: PostgreSQL, СУБД.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.