Изучая исходный код 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: сетевой протокол .