39.1. Дерево запроса
Чтобы понять, как работает система правил, нужно знать, когда она вызывается, что принимает на вход и какой результат выдаёт.
Система правил внедрена между анализатором запросов и планировщиком. Она принимает разобранный запрос, одно дерево запроса, и определённые пользователем правила перезаписи, тоже представленные деревьями с некоторой дополнительной информацией, и создаёт некоторое количество деревьев запросов в результате. Таким образом, на входе и выходе этой системы оказывается то, что может сформировать анализатор запросов, и как следствие, всё, с чем работает эта система, представимо в виде операторов SQL.
Так что же такое дерево запроса? Это внутреннее представление оператора SQL, в котором все образующие его части хранятся отдельно. Эти деревья можно увидеть в журнале сервера, если установить параметры конфигурации debug_print_parse, debug_print_rewritten или debug_print_plan. Действия правил также хранятся в виде деревьев запросов, в системном каталоге pg_rewrite. Они не форматируются как при выводе в журнал, но содержат точно такую же информацию.
Для прочтения неформатированного дерева требуется некоторый навык. Но так как представления дерева запросов в виде SQL достаточно, чтобы понять систему правил, в этой главе не будет рассказываться, как их читать.
Читая SQL-представления деревьев запросов в этой главе, необходимо понимать, на какие части разбивается оператор, когда он преобразуется в структуру дерева запроса. Дерево запроса состоит из следующих частей:
- тип команды
Это простое значение, говорящее, какая команда (
SELECT,INSERT,UPDATEилиDELETE) сгенерировала дерево запросов.- список отношений
Список отношений представляет собой массив отношений, используемых в запросе. В запросе
SELECTон включает отношения, указанные после ключевого словаFROM.Каждый элемент списка отношений представляет таблицу или представление и говорит, с каким именем они упоминаются в других частях запроса. В дереве запросов записываются номера элементов списка отношений, а не их имена, поэтому для него неактуальна проблема дублирования имён, как для оператора SQL. Такая проблема может возникнуть при объединении списков отношений, образованных разными правилами. В этой главе данная ситуация рассматриваться не будет.
- результирующее отношение
Индекс в списке отношений, указывающий на отношение, которое будет получать результаты запроса.
В запросах
SELECTрезультирующее отношение отсутствует. (Особый случайSELECT INTOпрактически равнозначенCREATE TABLEс последующимINSERT ... SELECTи здесь отдельно не рассматривается.)Для команд
INSERT,UPDATEиDELETEрезультирующим отношением будет таблица (или представление!), в которой будут происходить изменения.- выходной список
Выходной список — это список выражений, определяющих результат запроса. В случае
SELECT, это выражения, которые образуют окончательный набор выходных данных. Они соответствуют выражениям, записанным между ключевыми словамиSELECTиFROM. (Указание*— это просто краткое обозначение имён всех столбцов отношения. Анализатор разворачивает его в список отдельных столбцов, так что система правил никогда не видит его.)Командам
DELETEне нужен обычный выходной список, так как они не выдают никакие результаты. Вместо этого планировщик добавляет в пустой выходной список специальную запись CTID, чтобы исполнитель мог найти удаляемую строку. (CTID добавляется, когда результирующее отношение — обычная таблица. Если это представление, планировщиком добавляется переменная, содержащая всю строку, как рассказывается в Подразделе 39.2.4.)Для команд
INSERTвыходной список описывает новые строки, которые должны попасть в результирующее отношение. Он включает выражения в предложенииVALUESили предложенииSELECTвINSERT ... SELECT. На первом этапе процесс перезаписи добавляет элементы выходного списка для столбцов, которым ничего не присвоила исходная команда, но имеющих значения по умолчанию. Все остальные столбцы (без заданного значения и значения по умолчанию) планировщик заполняет константой NULL.Для команд
UPDATEвыходной список описывает новые строки, которые должны заменить старые. В системе правил он содержит только выражения из частиSET столбец = выражение. Для пропущенных столбцов планировщик вставляет выражения, копирующие значения из старой строки в новую. Так же, как и с командойDELETE, при этом добавляется CTID или переменная со всей строкой, чтобы исполнитель мог найти изменяемую старую строку.Каждая запись в выходном списке содержит выражение, которое может быть константой, переменной, указывающей на столбец отношения в таблице отношений, параметром или деревом выражений, образованным из констант, переменных, операторов, вызовов функций и т. д.
- условие фильтра
Условие фильтра запроса — это выражение, во многом похожее на те, что содержатся в выходном списке. Результат этого выражения — логический, он говорит, должна ли выполняться операция (
INSERT,UPDATE,DELETEилиSELECT) для данной строки в результате. Оно соответствует предложениюWHERESQL-оператора.- дерево соединения
Дерево соединения запроса показывает структуру предложения
FROM. Для простых запросов видаSELECT ... FROM a, b, c, дерево соединения — это просто список элементовFROM, так как они могут соединяться в любом порядке. Но с выражениямиJOIN, особенно с внешними соединениями, приходится соединять отношения именно в заданном порядке. В этом случае дерево соединения отражает структуру выраженийJOIN. Ограничения, связанные с конкретными предложениямиJOIN(из выраженийONилиUSING), тоже сохраняются в виде условных выражений, добавленных к соответствующим узлам дерева соединения. Как оказалось, выражениеWHEREверхнего уровня тоже удобно хранить как условие, добавленное к элементу верхнего уровня дерева соединения. Поэтому в дереве соединения на самом деле представляются оба предложения оператораSELECT—FROMиWHERE.- другие
Другие части дерева запроса, например, предложение
ORDER BY, в данном контексте не представляют интереса. Система правил выполняет в них некоторые подстановки, применяя правила, но это не имеет непосредственного отношения к основам системы правил.
39.1. The Query Tree
To understand how the rule system works it is necessary to know when it is invoked and what its input and results are.
The rule system is located between the parser and the planner. It takes the output of the parser, one query tree, and the user-defined rewrite rules, which are also query trees with some extra information, and creates zero or more query trees as result. So its input and output are always things the parser itself could have produced and thus, anything it sees is basically representable as an SQL statement.
Now what is a query tree? It is an internal representation of an SQL statement where the single parts that it is built from are stored separately. These query trees can be shown in the server log if you set the configuration parameters debug_print_parse, debug_print_rewritten, or debug_print_plan. The rule actions are also stored as query trees, in the system catalog pg_rewrite. They are not formatted like the log output, but they contain exactly the same information.
Reading a raw query tree requires some experience. But since SQL representations of query trees are sufficient to understand the rule system, this chapter will not teach how to read them.
When reading the SQL representations of the query trees in this chapter it is necessary to be able to identify the parts the statement is broken into when it is in the query tree structure. The parts of a query tree are
- the command type
This is a simple value telling which command (
SELECT,INSERT,UPDATE,DELETE) produced the query tree.- the range table
The range table is a list of relations that are used in the query. In a
SELECTstatement these are the relations given after theFROMkey word.Every range table entry identifies a table or view and tells by which name it is called in the other parts of the query. In the query tree, the range table entries are referenced by number rather than by name, so here it doesn't matter if there are duplicate names as it would in an SQL statement. This can happen after the range tables of rules have been merged in. The examples in this chapter will not have this situation.
- the result relation
This is an index into the range table that identifies the relation where the results of the query go.
SELECTqueries don't have a result relation. (The special case ofSELECT INTOis mostly identical toCREATE TABLEfollowed byINSERT ... SELECT, and is not discussed separately here.)For
INSERT,UPDATE, andDELETEcommands, the result relation is the table (or view!) where the changes are to take effect.- the target list
The target list is a list of expressions that define the result of the query. In the case of a
SELECT, these expressions are the ones that build the final output of the query. They correspond to the expressions between the key wordsSELECTandFROM. (*is just an abbreviation for all the column names of a relation. It is expanded by the parser into the individual columns, so the rule system never sees it.)DELETEcommands don't need a normal target list because they don't produce any result. Instead, the planner adds a special CTID entry to the empty target list, to allow the executor to find the row to be deleted. (CTID is added when the result relation is an ordinary table. If it is a view, a whole-row variable is added instead, by the rule system, as described in Section 39.2.4.)For
INSERTcommands, the target list describes the new rows that should go into the result relation. It consists of the expressions in theVALUESclause or the ones from theSELECTclause inINSERT ... SELECT. The first step of the rewrite process adds target list entries for any columns that were not assigned to by the original command but have defaults. Any remaining columns (with neither a given value nor a default) will be filled in by the planner with a constant null expression.For
UPDATEcommands, the target list describes the new rows that should replace the old ones. In the rule system, it contains just the expressions from theSET column = expressionpart of the command. The planner will handle missing columns by inserting expressions that copy the values from the old row into the new one. Just as forDELETE, a CTID or whole-row variable is added so that the executor can identify the old row to be updated.Every entry in the target list contains an expression that can be a constant value, a variable pointing to a column of one of the relations in the range table, a parameter, or an expression tree made of function calls, constants, variables, operators, etc.
- the qualification
The query's qualification is an expression much like one of those contained in the target list entries. The result value of this expression is a Boolean that tells whether the operation (
INSERT,UPDATE,DELETE, orSELECT) for the final result row should be executed or not. It corresponds to theWHEREclause of an SQL statement.- the join tree
The query's join tree shows the structure of the
FROMclause. For a simple query likeSELECT ... FROM a, b, c, the join tree is just a list of theFROMitems, because we are allowed to join them in any order. But whenJOINexpressions, particularly outer joins, are used, we have to join in the order shown by the joins. In that case, the join tree shows the structure of theJOINexpressions. The restrictions associated with particularJOINclauses (fromONorUSINGexpressions) are stored as qualification expressions attached to those join-tree nodes. It turns out to be convenient to store the top-levelWHEREexpression as a qualification attached to the top-level join-tree item, too. So really the join tree represents both theFROMandWHEREclauses of aSELECT.- the others
The other parts of the query tree like the
ORDER BYclause aren't of interest here. The rule system substitutes some entries there while applying rules, but that doesn't have much to do with the fundamentals of the rule system.