⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 arch-dev.sgml

📁 PostgreSQL7.4.6 for Linux
💻 SGML
📖 第 1 页 / 共 2 页
字号:
     transformation process be invoked.    </para>    <para>     The query tree created by the transformation process is structurally     similar to the raw parse tree in most places, but it has many differences     in detail.  For example, a <structname>FuncCall</> node in the     parse tree represents something that looks syntactically like a function     call.  This may be transformed to either a <structname>FuncExpr</>     or <structname>Aggref</> node depending on whether the referenced     name turns out to be an ordinary function or an aggregate function.     Also, information about the actual data types of columns and expression     results is added to the query tree.    </para>   </sect2>  </sect1>  <sect1 id="rule-system">   <title>The <productname>PostgreSQL</productname> Rule System</title>   <para>    <productname>PostgreSQL</productname> supports a powerful    <firstterm>rule system</firstterm> for the specification    of <firstterm>views</firstterm> and ambiguous <firstterm>view updates</firstterm>.    Originally the <productname>PostgreSQL</productname>    rule system consisted of two implementations:    <itemizedlist>     <listitem>      <para>       The first one worked using <firstterm>row level</firstterm> processing and was       implemented deep in the <firstterm>executor</firstterm>. The rule system was       called whenever an individual row had been accessed. This       implementation was removed in 1995 when the last official release       of the <productname>Berkeley Postgres</productname> project was       transformed into <productname>Postgres95</productname>.       </para>     </listitem>     <listitem>      <para>       The second implementation of the rule system is a technique       called <firstterm>query rewriting</firstterm>.       The <firstterm>rewrite system</firstterm> is a module       that exists between the <firstterm>parser stage</firstterm> and the       <firstterm>planner/optimizer</firstterm>. This technique is still implemented.      </para>     </listitem>    </itemizedlist>   </para>   <para>    The query rewriter is discussed in some detail in    <xref linkend="rules">, so there is no need to cover it here.    We will only point out that both the input and the output of the    rewriter are query trees, that is, there is no change in the    representation or level of semantic detail in the trees.  Rewriting    can be thought of as a form of macro expansion.   </para>  </sect1>  <sect1 id="planner-optimizer">   <title>Planner/Optimizer</title>   <para>    The task of the <firstterm>planner/optimizer</firstterm> is to    create an optimal execution plan. A given SQL query (and hence, a    query tree) can be actually executed in a wide variety of    different ways, each of which will produce the same set of    results.  If it is computationally feasible, the query optimizer    will examine each of these possible execution plans, ultimately    selecting the execution plan that will run the fastest.   </para>   <note>    <para>     In some situations, examining each possible way in which a query     may be executed would take an excessive amount of time and memory     space. In particular, this occurs when executing queries     involving large numbers of join operations. In order to determine     a reasonable (not optimal) query plan in a reasonable amount of     time, <productname>PostgreSQL</productname> uses a <xref     linkend="geqo" endterm="geqo-title">.    </para>   </note>   <para>    After the cheapest path is determined, a <firstterm>plan tree</>    is built to pass to the executor.  This represents the desired    execution plan in sufficient detail for the executor to run it.   </para>   <sect2>    <title>Generating Possible Plans</title>    <para>     The planner/optimizer decides which plans should be generated     based upon the types of indexes defined on the relations appearing in     a query. There is always the possibility of performing a     sequential scan on a relation, so a plan using only     sequential scans is always created. Assume an index is defined on a     relation (for example a B-tree index) and a query contains the     restriction     <literal>relation.attribute OPR constant</literal>. If     <literal>relation.attribute</literal> happens to match the key of the B-tree     index and <literal>OPR</literal> is one of the operators listed in     the index's <firstterm>operator class</>, another plan is created using     the B-tree index to scan the relation. If there are further indexes     present and the restrictions in the query happen to match a key of an     index further plans will be considered.    </para>    <para>     After all feasible plans have been found for scanning single relations,     plans for joining relations are created. The planner/optimizer     preferentially considers joins between any two relations for which there     exist a corresponding join clause in the <literal>WHERE</literal> qualification (i.e. for     which a restriction like <literal>where rel1.attr1=rel2.attr2</literal>     exists). Join pairs with no join clause are considered only when there     is no other choice, that is, a particular relation has no available     join clauses to any other relation. All possible plans are generated for     every join pair considered     by the planner/optimizer. The three possible join strategies are:     <itemizedlist>      <listitem>       <para>	<firstterm>nested loop join</firstterm>: The right relation is scanned	once for every row found in the left relation. This strategy	is easy to implement but can be very time consuming.  (However,	if the right relation can be scanned with an index scan, this can	be a good strategy.  It is possible to use values from the current	row of the left relation as keys for the index scan of the right.)       </para>      </listitem>      <listitem>       <para>	<firstterm>merge sort join</firstterm>: Each relation is sorted on the join	attributes before the join starts. Then the two relations are	merged together taking into account that both relations are	ordered on the join attributes. This kind of join is more	attractive because each relation has to be scanned only once.       </para>      </listitem>      <listitem>       <para>	<firstterm>hash join</firstterm>: the right relation is first scanned	and loaded into a hash table, using its join attributes as hash keys.	Next the left relation is scanned and the	appropriate values of every row found are used as hash keys to	locate the matching rows in the table.       </para>      </listitem>     </itemizedlist>    </para>    <para>     The finished plan tree consists of sequential or index scans of     the base relations, plus nested-loop, merge, or hash join nodes as     needed, plus any auxiliary steps needed, such as sort nodes or     aggregate-function calculation nodes.  Most of these plan node     types have the additional ability to do <firstterm>selection</>     (discarding rows that do not meet a specified boolean condition)     and <firstterm>projection</> (computation of a derived column set     based on given column values, that is, evaluation of scalar     expressions where needed).  One of the responsibilities of the     planner is to attach selection conditions from the     <literal>WHERE</literal> clause and computation of required     output expressions to the most appropriate nodes of the plan     tree.    </para>   </sect2>  </sect1>  <sect1 id="executor">   <title>Executor</title>   <para>    The <firstterm>executor</firstterm> takes the plan handed back by the    planner/optimizer and recursively processes it to extract the required set    of rows.  This is essentially a demand-pull pipeline mechanism.    Each time a plan node is called, it must deliver one more row, or    report that it is done delivering rows.   </para>   <para>    To provide a concrete example, assume that the top    node is a <literal>MergeJoin</literal> node.     Before any merge can be done two rows have to be fetched (one from    each subplan). So the executor recursively calls itself to    process the subplans (it starts with the subplan attached to    <literal>lefttree</literal>). The new top node (the top node of the left    subplan) is, let's say, a    <literal>Sort</literal> node and again recursion is needed to obtain    an input row.  The child node of the <literal>Sort</literal> might    be a <literal>SeqScan</> node, representing actual reading of a table.    Execution of this node causes the executor to fetch a row from the    table and return it up to the calling node.  The <literal>Sort</literal>    node will repeatedly call its child to obtain all the rows to be sorted.    When the input is exhausted (as indicated by the child node returning    a NULL instead of a row), the <literal>Sort</literal> code performs    the sort, and finally is able to return its first output row, namely    the first one in sorted order.  It keeps the remaining rows stored so    that it can deliver them in sorted order in response to later demands.   </para>   <para>    The <literal>MergeJoin</literal> node similarly demands the first row    from its right subplan.  Then it compares the two rows to see if they    can be joined; if so, it returns a join row to its caller.  On the next    call, or immediately if it cannot join the current pair of inputs,    it advances to the next row of one table    or the other (depending on how the comparison came out), and again    checks for a match.  Eventually, one subplan or the other is exhausted,    and the <literal>MergeJoin</literal> node returns NULL to indicate that    no more join rows can be formed.   </para>   <para>    Complex queries may involve many levels of plan nodes, but the general    approach is the same: each node computes and returns its next output    row each time it is called.  Each node is also responsible for applying    any selection or projection expressions that were assigned to it by    the planner.   </para>   <para>    The executor mechanism is used to evaluate all four basic SQL query types:    <command>SELECT</>, <command>INSERT</>, <command>UPDATE</>, and    <command>DELETE</>.  For <command>SELECT</>, the top-level executor    code only needs to send each row returned by the query plan tree off    to the client.  For <command>INSERT</>, each returned row is inserted    into the target table specified for the <command>INSERT</>.  (A simple    <command>INSERT ... VALUES</> command creates a trivial plan tree    consisting of a single <literal>Result</> node, which computes just one    result row.  But <command>INSERT ... SELECT</> may demand the full power    of the executor mechanism.)  For <command>UPDATE</>, the planner arranges    that each computed row includes all the updated column values, plus    the <firstterm>TID</> (tuple ID, or row ID) of the original target row;    the executor top level uses this information to create a new updated row    and mark the old row deleted.  For <command>DELETE</>, the only column    that is actually returned by the plan is the TID, and the executor top    level simply uses the TID to visit the target rows and mark them deleted.   </para>  </sect1> </chapter><!-- Keep this comment at the end of the fileLocal variables:mode:sgmlsgml-omittag:nilsgml-shorttag:tsgml-minimize-attributes:nilsgml-always-quote-attributes:tsgml-indent-step:1sgml-indent-data:tsgml-parent-document:nilsgml-default-dtd-file:"./reference.ced"sgml-exposed-tags:nilsgml-local-catalogs:("/usr/lib/sgml/catalog")sgml-local-ecat-files:nilEnd:-->

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -