The FDW callback functions <function>GetForeignRelSize</function>,
<function>GetForeignPaths</function>, <function>GetForeignPlan</function>,
<function>PlanForeignModify</function>, <function>GetForeignJoinPaths</function>,
<function>GetForeignUpperPaths</function>, and <function>PlanDirectModify</function>
must fit into the workings of the <productname>PostgreSQL</productname> planner.
Here are some notes about what they must do.
FDWコールバック関数のGetForeignRelSize
、GetForeignPaths
、GetForeignPlan
、PlanForeignModify
、GetForeignJoinPaths
、GetForeignUpperPaths
、PlanDirectModify
はPostgreSQLプランナの動作と協調しなければなりません。ここでは、これらの関数がすべき事に関するいくつかの注意事項を述べます。
The information in <literal>root</literal> and <literal>baserel</literal> can be used
to reduce the amount of information that has to be fetched from the
foreign table (and therefore reduce the cost).
<literal>baserel->baserestrictinfo</literal> is particularly interesting, as
it contains restriction quals (<literal>WHERE</literal> clauses) that should be
used to filter the rows to be fetched. (The FDW itself is not required
to enforce these quals, as the core executor can check them instead.)
<literal>baserel->reltarget->exprs</literal> can be used to determine which
columns need to be fetched; but note that it only lists columns that
have to be emitted by the <structname>ForeignScan</structname> plan node, not
columns that are used in qual evaluation but not output by the query.
root
とbaserel
に含まれる情報は、外部テーブルから取得する必要のある情報の量(とそれによるコスト)を削減するために使用できます。
baserel->baserestrictinfo
は、取得される行をフィルタリングする検索条件(WHERE
句)を含んでいるため、特に興味深いものです。(コアのエグゼキュータが代わりにそれらをチェックできるので、FDW自身がこれらの制約を適用しなければならないわけではありません。)
baserel->reltarget->exprs
はどの列が取得される必要があるかを決定するのに使用できます。ただし、このリストはForeignScan
プランノードから出力すべき列しか含んでおらず、条件検査には必要だが問い合わせからは出力されない列は含まないことに注意してください。
Various private fields are available for the FDW planning functions to keep information in. Generally, whatever you store in FDW private fields should be palloc'd, so that it will be reclaimed at the end of planning. 様々なプライベートフィールドがFDWのプラン作成関数で情報を格納する目的で利用できます。 一般的に、プラン作成の最後に回収できるように、FDW固有フィールドに格納するものは全てpallocで確保すべきです。
<literal>baserel->fdw_private</literal> is a <type>void</type> pointer that is
available for FDW planning functions to store information relevant to
the particular foreign table. The core planner does not touch it except
to initialize it to NULL when the <literal>RelOptInfo</literal> node is created.
It is useful for passing information forward from
<function>GetForeignRelSize</function> to <function>GetForeignPaths</function> and/or
<function>GetForeignPaths</function> to <function>GetForeignPlan</function>, thereby
avoiding recalculation.
baserel->fdw_private
は、void
ポインタで、FDWのプラン作成関数で特定の外部テーブルに関する情報を格納する目的で利用できます。
コアプランナは、RelOptInfo
ノードが作成されるときにNULLで初期化するときを除いて、このフィールドに一切に触れません。
このフィールドは、GetForeignRelSize
からGetForeignPaths
やGetForeignPaths
からGetForeignPlan
といったように情報を順次伝えるの便利で、結果として再計算を省くことができます。
<function>GetForeignPaths</function> can identify the meaning of different
access paths by storing private information in the
<structfield>fdw_private</structfield> field of <structname>ForeignPath</structname> nodes.
<structfield>fdw_private</structfield> is declared as a <type>List</type> pointer, but
could actually contain anything since the core planner does not touch
it. However, best practice is to use a representation that's dumpable
by <function>nodeToString</function>, for use with debugging support available
in the backend.
GetForeignPaths
では、ForeignPath
ノードのfdw_private
フィールドに固有情報を格納することで、異なるアクセスパスを区別できます。fdw_private
はList
ポインタとして宣言されていますが、コアプランナがこのフィールドを操作することはないため、実際にはなんでも格納できます。
しかし、バックエンドのデバッグサポート機能を利用できるようにnodeToString
でダンプ出来る形式を使うのが最良の手法です。
<function>GetForeignPlan</function> can examine the <structfield>fdw_private</structfield>
field of the selected <structname>ForeignPath</structname> node, and can generate
<structfield>fdw_exprs</structfield> and <structfield>fdw_private</structfield> lists to be
placed in the <structname>ForeignScan</structname> plan node, where they will be
available at execution time. Both of these lists must be
represented in a form that <function>copyObject</function> knows how to copy.
The <structfield>fdw_private</structfield> list has no other restrictions and is
not interpreted by the core backend in any way. The
<structfield>fdw_exprs</structfield> list, if not NIL, is expected to contain
expression trees that are intended to be executed at run time. These
trees will undergo post-processing by the planner to make them fully
executable.
GetForeignPlan
では、選択されたForeignPath
ノードのfdw_private
フィールドを調べて、ForeignScan
プランノード内に格納されプラン実行時に利用可能なfdw_exprs
とfdw_private
の二つのリストを生成することができます。
これらは両方ともcopyObject
がコピーできる形式でなければなりません。
fdw_private
リストにはこれ以外に制約はなく、コアバックエンドによって解釈されることはありません。
fdw_exprs
リストがNILでない場合は、問い合わせ実行時に実行されることを意図した式ツリーが含まれていることが期待されます。
これらのツリーは、完全に実行可能な状態にするためにプランナによる後処理を受けます。
In <function>GetForeignPlan</function>, generally the passed-in target list can
be copied into the plan node as-is. The passed <literal>scan_clauses</literal> list
contains the same clauses as <literal>baserel->baserestrictinfo</literal>,
but may be re-ordered for better execution efficiency. In simple cases
the FDW can just strip <structname>RestrictInfo</structname> nodes from the
<literal>scan_clauses</literal> list (using <function>extract_actual_clauses</function>) and put
all the clauses into the plan node's qual list, which means that all the
clauses will be checked by the executor at run time. More complex FDWs
may be able to check some of the clauses internally, in which case those
clauses can be removed from the plan node's qual list so that the
executor doesn't waste time rechecking them.
GetForeignPlan
では、一般的に渡されたターゲットリストはそのままプランノードにコピーできます。
渡されたscan_clauses
リストはbaserel->baserestrictinfo
と同じ句を含みますが、実行効率のよい別の順番に並べ替えることもできます。
FDWにできるのがRestrictInfo
ノードをscan_clauses
リストから(extract_actual_clauses
を使って)抜き出して、全ての句をプランノードの条件リストに入れるだけ、といった単純なケースでは、全ての句は実行時にエグゼキュータによってチェックされます。
より複雑なFDWは内部で一部の句をチェックできるかもしれませんが、そのような場合には、エグゼキュータが再チェックのために時間を無駄にしないように、それらの句はプランノードの条件リストから削除できます。
As an example, the FDW might identify some restriction clauses of the
form <replaceable>foreign_variable</replaceable> <literal>=</literal>
<replaceable>sub_expression</replaceable>, which it determines can be executed on
the remote server given the locally-evaluated value of the
<replaceable>sub_expression</replaceable>. The actual identification of such a
clause should happen during <function>GetForeignPaths</function>, since it would
affect the cost estimate for the path. The path's
<structfield>fdw_private</structfield> field would probably include a pointer to
the identified clause's <structname>RestrictInfo</structname> node. Then
<function>GetForeignPlan</function> would remove that clause from <literal>scan_clauses</literal>,
but add the <replaceable>sub_expression</replaceable> to <structfield>fdw_exprs</structfield>
to ensure that it gets massaged into executable form. It would probably
also put control information into the plan node's
<structfield>fdw_private</structfield> field to tell the execution functions what
to do at run time. The query transmitted to the remote server would
involve something like <literal>WHERE <replaceable>foreign_variable</replaceable> =
$1</literal>, with the parameter value obtained at run time from
evaluation of the <structfield>fdw_exprs</structfield> expression tree.
たとえば、ローカル側で評価されたsub_expression
の値があればリモートサーバ側で実行出来るとFDWが判断するような、foreign_variable
=
sub_expression
といった形式の条件句をFDWが識別するかもしれません。
パスのコスト見積もりに影響するので、そのような句の実際の識別はGetForeignPaths
でなされるべきです。
おそらく、そのパスのfdw_private
フィールドは識別された句のRestrictInfo
ノードをさすポインタを含むでしょう。
そして、GetForeignPlan
はその句をscan_clauses
から取り除き、実行可能な形式にほぐされることを保障するためにsub_expression
をfdw_exprs
に追加するでしょう。
また、おそらく、実行時に何をすべきかをプラン実行関数に伝えるためにプランノードのfdw_private
フィールドに制御情報を入れるでしょう。
リモートサーバに送られた問い合わせは、実行時にfdw_exprs
式ツリーを評価して得られた値をパラメータ値とするWHERE
のようなものを伴うでしょう。
foreign_variable
= $1
Any clauses removed from the plan node's qual list must instead be added
to <literal>fdw_recheck_quals</literal> or rechecked by
<literal>RecheckForeignScan</literal> in order to ensure correct behavior
at the <literal>READ COMMITTED</literal> isolation level. When a concurrent
update occurs for some other table involved in the query, the executor
may need to verify that all of the original quals are still satisfied for
the tuple, possibly against a different set of parameter values. Using
<literal>fdw_recheck_quals</literal> is typically easier than implementing checks
inside <literal>RecheckForeignScan</literal>, but this method will be
insufficient when outer joins have been pushed down, since the join tuples
in that case might have some fields go to NULL without rejecting the
tuple entirely.
READ COMMITTED
分離レベルでの正しい動作を保証するため、プランノードの条件リストから除かれた句はすべて、代わりにfdw_recheck_quals
に追加されるか、RecheckForeignScan
で再検査される必要があります。
問い合わせに含まれる他のテーブルで同時更新があった場合、エグゼキュータはタプルが元の条件を、それも場合によっては異なるパラメータ値の組み合わせに対して満たすことを確認する必要があるかもしれません。
fdw_recheck_quals
を使うのは、RecheckForeignScan
の内部で検査を実装するより、通常は簡単でしょう。
しかしこの方法は、外部結合がプッシュダウンされる場合は不十分です。
なぜなら、この場合の結合タプルはタプル全体を拒絶せずに、一部のフィールドをNULLにしてしまうからです。
Another <structname>ForeignScan</structname> field that can be filled by FDWs
is <structfield>fdw_scan_tlist</structfield>, which describes the tuples returned by
the FDW for this plan node. For simple foreign table scans this can be
set to <literal>NIL</literal>, implying that the returned tuples have the
row type declared for the foreign table. A non-<symbol>NIL</symbol> value must be a
target list (list of <structname>TargetEntry</structname>s) containing Vars and/or
expressions representing the returned columns. This might be used, for
example, to show that the FDW has omitted some columns that it noticed
won't be needed for the query. Also, if the FDW can compute expressions
used by the query more cheaply than can be done locally, it could add
those expressions to <structfield>fdw_scan_tlist</structfield>. Note that join
plans (created from paths made by <function>GetForeignJoinPaths</function>) must
always supply <structfield>fdw_scan_tlist</structfield> to describe the set of
columns they will return.
FDWがセットできる別のForeignScan
フィールドにfdw_scan_tlist
があります。
これはこのプランノードについてFDWが返すタプルを記述するものです。
単純な外部テーブルスキャンに対しては、これをNIL
にセットすることができ、それは戻されるタプルが外部テーブルで宣言された行型を持つことを意味します。
NIL
でない値はVar型の変数、あるいは返される列を表す式、あるいはその両方を含む対象のリスト(TargetEntry
のリスト)でなければなりません。
これは例えば、FDWが問い合わせのために必要ないと気づいた列を無視したことを示すのに使えるかもしれません。
また、FDWが問い合わせで使われる式をローカルで計算するより安価に計算できるなら、それらの式をfdw_scan_tlist
に追加することができます。
結合プラン(GetForeignJoinPaths
が作るパスから作成される)は、それが返す列の集合を記述するfdw_scan_tlist
を必ず提供しなければならないことに注意して下さい。
The FDW should always construct at least one path that depends only on
the table's restriction clauses. In join queries, it might also choose
to construct path(s) that depend on join clauses, for example
<replaceable>foreign_variable</replaceable> <literal>=</literal>
<replaceable>local_variable</replaceable>. Such clauses will not be found in
<literal>baserel->baserestrictinfo</literal> but must be sought in the
relation's join lists. A path using such a clause is called a
<quote>parameterized path</quote>. It must identify the other relations
used in the selected join clause(s) with a suitable value of
<literal>param_info</literal>; use <function>get_baserel_parampathinfo</function>
to compute that value. In <function>GetForeignPlan</function>, the
<replaceable>local_variable</replaceable> portion of the join clause would be added
to <structfield>fdw_exprs</structfield>, and then at run time the case works the
same as for an ordinary restriction clause.
FDWはそのテーブルの条件句のみに依存するパスを常に少なくとも一つは生成すべきです。結合問い合わせでは、例えばforeign_variable
=
local_variable
といった結合句に依存するパス(群)を生成することもできます。
そのような句はbaserel->baserestrictinfo
には見つからず、リレーションの結合リストにあるはずです。
そのような句を使用するパスは「パラメータ化されたパス」と呼ばれます。
このようなパスでは、選択された結合句(群)で使用されているリレーション(群)をparam_info
の適合する値から特定しなければなりません;その値を計算するにはget_baserel_parampathinfo
を使用します。
GetForeignPlan
では、結合句のlocal_variable
部分がfdw_exprs
に追加され、実行時には通常の条件句と同じように動作します。
If an FDW supports remote joins, <function>GetForeignJoinPaths</function> should
produce <structname>ForeignPath</structname>s for potential remote joins in much
the same way as <function>GetForeignPaths</function> works for base tables.
Information about the intended join can be passed forward
to <function>GetForeignPlan</function> in the same ways described above.
However, <structfield>baserestrictinfo</structfield> is not relevant for join
relations; instead, the relevant join clauses for a particular join are
passed to <function>GetForeignJoinPaths</function> as a separate parameter
(<literal>extra->restrictlist</literal>).
FDWがリモートでの結合をサポートする場合、GetForeignPaths
がベーステーブルに対して処理するのとほぼ同じように、GetForeignJoinPaths
は潜在的なリモートの結合に対してForeignPath
を生成することになります。
意図した結合に関する情報は、上記と同じ方法でGetForeignPlan
に送ることができます。
しかし、baserestrictinfo
は結合のリレーションには関連がなく、代わりに、特定の結合に関連するJOIN句はGetForeignJoinPaths
に別のパラメータ(extra->restrictlist
)として渡されます。
An FDW might additionally support direct execution of some plan actions
that are above the level of scans and joins, such as grouping or
aggregation. To offer such options, the FDW should generate paths and
insert them into the appropriate <firstterm>upper relation</firstterm>. For
example, a path representing remote aggregation should be inserted into
the <literal>UPPERREL_GROUP_AGG</literal> relation, using <function>add_path</function>.
This path will be compared on a cost basis with local aggregation
performed by reading a simple scan path for the foreign relation (note
that such a path must also be supplied, else there will be an error at
plan time). If the remote-aggregation path wins, which it usually would,
it will be converted into a plan in the usual way, by
calling <function>GetForeignPlan</function>. The recommended place to generate
such paths is in the <function>GetForeignUpperPaths</function>
callback function, which is called for each upper relation (i.e., each
post-scan/join processing step), if all the base relations of the query
come from the same FDW.
FDWはグルーピングや集約のような、スキャンや結合のレベルより上位のプラン動作の直接実行を追加的にサポートできるかもしれません。
このような方法を行うには、FDWはパスを生成して、それを適切な上位リレーションに挿入する必要があります。
例えば、リモート集約をあらわすパスはadd_path
を使ってUPPERREL_GROUP_AGG
リレーションに挿入されるべきです。
このパスは外部リレーションに対する単純なスキャンパスを読むことによるローカル集約実行とコストに基づいて比較されます(このようなパスが提供されなければならないことに注意してください、さもないとプラン時にエラーになります)。
リモート集約パスが、通常そうなりますが、勝った場合には、パスはGetForeignPlan
を呼ぶ通常の手段でプランに変換されます。
もし問い合わせの全てのベースリレーションが同じFDWから来るなら、このようなパスを生成するのに推奨される場所は、各上位リレーション(すなわち各スキャン/結合後の処理の段階)に対して呼び出されるGetForeignUpperPaths
コールバック関数の中です。
<function>PlanForeignModify</function> and the other callbacks described in
<xref linkend="fdw-callbacks-update"/> are designed around the assumption
that the foreign relation will be scanned in the usual way and then
individual row updates will be driven by a local <literal>ModifyTable</literal>
plan node. This approach is necessary for the general case where an
update requires reading local tables as well as foreign tables.
However, if the operation could be executed entirely by the foreign
server, the FDW could generate a path representing that and insert it
into the <literal>UPPERREL_FINAL</literal> upper relation, where it would
compete against the <literal>ModifyTable</literal> approach. This approach
could also be used to implement remote <literal>SELECT FOR UPDATE</literal>,
rather than using the row locking callbacks described in
<xref linkend="fdw-callbacks-row-locking"/>. Keep in mind that a path
inserted into <literal>UPPERREL_FINAL</literal> is responsible for
implementing <emphasis>all</emphasis> behavior of the query.
PlanForeignModify
と57.2.4で記述された他のコールバックは、外部リレーションは通常の方法でスキャンされ、それから個別の行変更がローカルのModifyTable
プランノードで駆動されるという想定をもとに設計されています。
この方法は変更が外部テーブルと同様にローカルテーブルを読む必要がある一般的な場合に必要です。
しかしながら、操作が全体的に外部サーバで実行できるなら、FDWはそのようにするパスを生成してUPPERREL_FINAL
上位リレーションに挿入することができます。ここではModifyTable
方式に対して競合します。
この方式は、57.2.6で記述された行ロックコールバックを使うのでなしに、リモートSELECT FOR UPDATE
を実装するのにも使われます。
UPPERREL_FINAL
に挿入されたパスは問い合わせの全ての振る舞いの実装に責任があることに留意してください。
When planning an <command>UPDATE</command> or <command>DELETE</command>,
<function>PlanForeignModify</function> and <function>PlanDirectModify</function>
can look up the <structname>RelOptInfo</structname>
struct for the foreign table and make use of the
<literal>baserel->fdw_private</literal> data previously created by the
scan-planning functions. However, in <command>INSERT</command> the target
table is not scanned so there is no <structname>RelOptInfo</structname> for it.
The <structname>List</structname> returned by <function>PlanForeignModify</function> has
the same restrictions as the <structfield>fdw_private</structfield> list of a
<structname>ForeignScan</structname> plan node, that is it must contain only
structures that <function>copyObject</function> knows how to copy.
UPDATE
やDELETE
のプランを生成しているとき、
PlanForeignModify
とPlanDirectModify
は、事前にスキャンプラン生成関数で作られたbaserel->fdw_private
データを使うために、その外部テーブルのためのRelOptInfo
構造体を検索することができます。
しかしながら、INSERT
では対象テーブルはスキャンされないので対応するRelOptInfo
は存在しません。
PlanForeignModify
から返されるList
には、ForeignScan
プランノードのfdw_private
リストと同様に、copyObject
がコピーの仕方を知っている構造体しか保持してはいけないという制約があります。
<command>INSERT</command> with an <literal>ON CONFLICT</literal> clause does not
support specifying the conflict target, as unique constraints or
exclusion constraints on remote tables are not locally known. This
in turn implies that <literal>ON CONFLICT DO UPDATE</literal> is not supported,
since the specification is mandatory there.
ON CONFLICT
句のあるINSERT
は競合の対象の指定をサポートしません。
なぜなら、リモートのテーブルの一意制約や排他制約についての情報がローカルにはないからです。
これは結果的にON CONFLICT DO UPDATE
がサポートされないことを意味します。
なぜなら、競合の対象の指定が必須だからです。