50.6. Исполнитель

Исполнитель принимает план, созданный планировщиком/исполнителем и обрабатывает его рекурсивно, чтобы получить требуемый набор строк. Обработка выполняется по конвейеру, с получением данных по требованию. При вызове любого узла плана он должен выдать очередную строку, либо сообщить, что выдача строк завершена.

В качестве более конкретного примера, давайте предположим, что верхним узлом плана оказался узел MergeJoin. Для того чтобы выполнить какое-либо соединение, необходимо выбрать две строки (одну из каждого вложенного плана). Поэтому исполнитель рекурсивно вызывает себя для обработки вложенных планов (он начинает с плана левого дерева). Новый верхний узел (верхний узел левого вложенного плана) может быть, например, узлом Sort, и тогда для получения входной строки снова требуется рекурсия. Дочерним узлом Sort может быть узел SeqScan, представляющий собственно чтение таблицы. В результате выполнения этого узла исполнитель выбирает одну строку из таблицы и возвращает её вызывающему узлу. Узел Sort, в свою очередь, будет продолжать вызывать дочерний узел, пока не получит все строки для сортировки. Когда строки закончатся (дочерний узел сообщит об этом, возвратив NULL вместо строки), узел Sort выполнит сортировку, и наконец сможет выдать свою первую строку, а именно строку первую по порядку сортировки. Остальные строки будут сохраняться в нём, чтобы он мог выдавать их по порядку при последующих вызовах.

Узел MergeJoin подобным образом затребует первую строку и у вложенного плана справа. Затем он сравнивает две строки и определяет, можно ли их соединить; если да, он возвращает соединённую строки вызывающему узлу. При следующем вызове, или немедленно, если он не может соединить текущую пару поступивших строк, он переходит к следующей строке в одном отношении или в другом (в зависимости от результата сравнения) и снова проверяет соответствие. В конце концов, данные в одном или другом вложенном плане заканчиваются и узел MergeJoin возвращает NULL, показывая тем самым, что другие строки соединения получить нельзя.

Сложные запросы могут содержать много уровней вложенности узлов плана, но общий подход тот же: каждый узел вычисляет и возвращает следующую полученную строку при очередном вызове. Каждый узел также должен производить отбор и расчёты, которые были назначены ему планировщиком.

Механизм исполнителя применяется для обработки всех четырёх основных типов SQL-запросов: SELECT, INSERT, UPDATE и DELETE. С SELECT код исполнителя верхнего уровня должен только выдать клиенту все строки, полученные от дерева плана запроса. С INSERT все полученные строки вставляются в целевую таблицу INSERT. Эта операция выполняется в специальном узле на верхнем уровне плана запроса, называемом ModifyTable. (Простая команда INSERT ... VALUES создаёт простейшее дерево плана, состоящее из одного узла Result, который вычисляет одну строку результата, и узла ModifyTable над ним, который осуществляет добавление. Но с INSERT ... SELECT могут быть востребованы все возможности механизма исполнителя.) С UPDATE планировщик делает так, чтобы каждая вычисленная строка включала значения всех изменённых столбцов плюс TID (Tuple ID, идентификатор кортежа) исходной целевой строки; эти данные подаются в узел ModifyTable, который использует эту информацию, чтобы создать новую изменённую строку и пометить старую строку как удалённую. С DELETE план фактически возвращает только один столбец, TID, а узел ModifyTable использует значение TID, чтобы найти каждую целевую строку и пометить её как удалённую.