← На главную

Внутренности PostgreSQL: что такое Portal

Изучая исходный код PostgreSQL, можно повстречать сущность под названием Portal. Возникает закономерный вопрос – что это такое, а также где и для чего используется? Попробуем разобраться.

Чтобы вживую увидеть Portal, подключимся gdb к бэкенду PostgreSQL и поставим следующую точку останова:

(gdb) b DefineRelation (gdb) c

Если теперь выполнить запрос:

CREATE TABLE "data"("id" INT);

… то будет получен такой стэктрейс:

(gdb) bt #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 portalContext;

Портал имеет выделенный ему MemoryContext. C MemoryContext’ами к этому моменту мы уже знакомы.

ResourceOwner resowner;

Также каждому порталу присваивается ResourceOwner. Это отдельная важная структура, однако ее обсуждение выходит за рамки поста. Если в двух словах, то ResourceOwner отвечает за своевременное освобождение ресурсов, таких как открытые файлы, pin’ы на разделяемые буферы, и так далее.

void (*cleanup) (Portal portal);

Хук, вызываемый при освобождении портала. Как правило, это PortalCleanup(), объявленный в portalcmds.c.

const char *sourceText;

Текст SQL-запроса. В PostgreSQL ≥ 8.4 здесь не может быть NULL.

ParamListInfo portalParams;

Параметры запроса.

Чтобы посмотреть, как эта структура заполняется, в psql выполним:

PREPARE foo(INT) AS SELECT $1;

Затем создадим точку останова на PortalRun() и выполним запрос:

EXECUTE foo(123);

Точка останова срабатывает дважды:

#0 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

Значение полей при первом вызове функции:

(gdb) p portal->sourceText $1 = 0x5555c69b31c0 "EXECUTE foo(123);" (gdb) p portal->portalParams $2 = (ParamListInfo) 0x0

… и при втором:

(gdb) p portal->sourceText $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: сетевой протокол.