FDWハンドラ関数は、以下で説明するコールバック関数へのポインタを含む、pallocされたFdwRoutine
構造体を返します。
スキャンに関連した関数は必須で、それ以外は省略可能です。
FdwRoutine
構造体はsrc/include/foreign/fdwapi.h
で宣言されていますので、追加情報はそちらを参照してください。
void GetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
外部テーブルのリレーションサイズ見積もりを取得します。この関数は、ある外部テーブルをスキャンするクエリのプラン作成の開始時に呼び出されます。root
はそのクエリに関するプランナのグローバル情報です。baserel
はそのテーブルに関するプランナの情報です。そして、foreigntableid
はその外部テーブルのpg_class
OIDです。(foreigntableid
はプランナデータ構造体からも取得できますが、手間を省くために明示的に渡されます。)
この関数は、制約条件によるフィルタリングも考慮に入れた、そのテーブルスキャンが返すと見込まれる件数にbaserel->rows
を更新するべきです。
baserel->rows
の初期値は固定のデフォルト見積もりなので、可能な限り置き換えられるべきです。この関数は、行の幅のよりよい見積もりを計算できるのであれば、baserel->width
を更新することも選択出来ます。
追加情報については55.4. 外部データラッパのクエリプラン作成を参照してください。
void GetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
外部テーブル対するスキャンとしてありえるアクセスパスを作成します。
この関数はクエリのプラン作成中に呼び出されます。
引数は、先に呼ばれているGetForeignRelSize
と同じです。
この関数は、少なくとも一つのアクセスパス(ForeignPath
ノード)を作成して、それぞれのパスをbaserel->pathlist
に追加するためにadd_path
を呼ばなければなりません。
ForeignPath
ノードを構築するにはcreate_foreignscan_path
を使うことが推奨されています。
この関数は、たとえばソート済みの結果を表現する有効なpathkeys
を持つパスのような複数のアクセスパスを作成することが出来ます。
それぞれのアクセスパスはコスト見積もりを含まねばならず、また意図した特定のスキャン方式を識別するのに必要なFDW固有の情報を持つことが出来ます。
追加情報については55.4. 外部データラッパのクエリプラン作成を参照してください。
ForeignScan * GetForeignPlan (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
選択された外部アクセスパスからForeignScan
プランノードを作成します。
この関数はクエリプラン作成の最後に呼び出されます。
引数は、GetForeignRelSize
と同じものに、選択されたForeignPath
(事前にGetForeignPaths
、GetForeignJoinPaths
またはGetForeignUpperPaths
によって作成されたもの)、そのプランノードによって出力されるターゲットリスト、そのプランノードで強制される条件句、およびRecheckForeignScan
が実行する再検査で使用されるForeignScan
の外側のサブプランが追加されます。
(パスがベースリレーションではなく結合のためのものの場合、foreigntableid
はInvalidOid
になります。)
この関数はForeignScan
プランノードを作成して返さなければなりません。ForeignScan
ノードを構築するにはmake_foreignscan
を使うことが推奨されています。
追加情報については55.4. 外部データラッパのクエリプラン作成を参照してください。
void BeginForeignScan (ForeignScanState *node, int eflags);
外部テーブルスキャンの実行を開始します。
この関数はエグゼキュータの起動中に呼び出されます。
スキャンを開始できるようになる前に、あらゆる必要な初期化を実行するべきですが、実際のスキャンの実行を始めるべきではありません(それは最初のIterateForeignScan
呼び出しにおいて行われるべきです)。
ForeignScanState
ノードは作成されていますが、そのfdw_state
フィールドはNULLのままです。
スキャンするテーブルの情報は、ForeignScanState
ノード(実際にはその先にある、PlanForeignScan
から返されたFdwPlan
構造体へのポインタを含む、ForeignScan
プランノード)を通じてアクセス可能です。
eflags
は、このプランノードに関するエグゼキュータの操作モードを表すフラグビットを含みます。
(eflags & EXEC_FLAG_EXPLAIN_ONLY)
が真の場合、この関数は外部に見える処理を実行すべきではないことに注意してください。
ExplainForeignScan
やEndForeignScan
用にノード状態を有効にするのに必要とされる最小限のことだけをすべきです。
TupleTableSlot * IterateForeignScan (ForeignScanState *node);
外部ソースから一行を取り出して、それをタプルテーブルスロットに入れて返します(この用途にはnodeのScanTupleSlot
を使うべきです)。
利用可能な行がない場合は、NULLを返します。
タプルテーブルスロット機構を使うと、物理タプルと仮想タプルのどちらでも返せます。
ほとんどの場合、パフォーマンスの観点から後者を選ぶのが良いでしょう。
この関数は、呼出しごとにリセットされる短命なメモリコンテキスト内で呼び出されることに注意してください。
より長命なストレージが必要な場合は、BeginForeignScan
でメモリコンテキストを作成するか、ノードのEState
に含まれるes_query_cxt
を使用してください。
返される行は、ターゲットリストfdw_scan_tlist
が提供されたなら、それとマッチしなければならず、提供されていない場合はスキャンされている外部テーブルの行型とマッチしなければなりません。
不要な列を取り出さないように最適化することを選ぶなら、それらの列の位置にNULLを入れるか、あるいはそれらの列を除いたfdw_scan_tlist
リストを生成するべきです。
PostgreSQLのエグゼキュータは返された行が外部テーブルに定義された制約に違反しているかどうかは気にしません。 しかし、プランナはそれに着目するので、宣言された制約に反する行が外部テーブル上にあった場合に、不正な最適化をするかもしれません。 ユーザが制約が成り立つと宣言したのに制約に違反した場合は(データ型が一致しなかった場合にする必要があるのと同様に)エラーを発生させるのが適切でしょう。
void ReScanForeignScan (ForeignScanState *node);
先頭からスキャンを再開します。 スキャンが依存するいずれかのパラメータが値を変更しているかもしれないので、新しいスキャンが必ずしも厳密に同じ行を返すとは限らないことに注意してください。
void EndForeignScan (ForeignScanState *node);
スキャンを終了しリソースを解放します。 通常、pallocされたメモリを解放することは重要ではありませんが、開いたファイルやリモートサーバへの接続などはクリーンアップするべきです。
FDWが外部テーブルの結合を(両方のテーブルのデータをフェッチして、ローカルで結合するのでなく)リモートで実行することをサポートする場合、次のコールバック関数を提供します。
void GetForeignJoinPaths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra);
同じ外部サーバにある2つ(またはそれ以上)の外部テーブルの結合のための可能なアクセスパスを作成します。
このオプション関数は、問い合わせの計画時に呼び出されます。
GetForeignPaths
と同じく、この関数は提供されたjoinrel
のためのForeignPath
パスを生成し、add_path
を呼んで、それらのパスを結合のために考慮されるパスの集合に追加します。
しかし、GetForeignPaths
とは異なり、この関数が少なくとも1つのパスの作成に成功することは必要ではありません。
なぜなら、ローカルの結合を含んだパスはいつでも可能だからです。
この関数は、同じ結合のリレーションに対して、内側と外側のリレーションの異なる組み合わせで繰り返し呼び出されることに注意して下さい。 同じ作業の繰り返しを最小化することはFDWの責任です。
ForeignPath
パスが結合のために選択されると、それは結合プロセス全体を代表することになり、構成テーブルとその関連の結合のために生成されたパスは使われなくなります。
結合パスの以降の処理は、単一の外部テーブルをスキャンするパスとほぼ同様に進みます。
1つの相違点は、結果として作られるForeignScan
計画ノードのscanrelid
が0にセットされるべき、ということで、これはそれが表現する単一のリレーションがないためです。
その代わりに、ForeignScan
ノードのfs_relids
フィールドが結合されるリレーションの集合を表します。
(後者のフィールドはコアのプランナのコードによって自動的にセットされるので、FDWによって設定される必要はありません。)
他の相違点は、リモートの結合についての列リストがシステムカタログにはないため、FDWはfdw_scan_tlist
に適切なTargetEntry
ノードのリストを入れて、実行時に返されるタプル内の列の集合を表すようにしなければならないということです。
追加情報については55.4. 外部データラッパのクエリプラン作成を参照してください。
FDWがリモート集約など、リモートでのスキャン/結合後の処理をサポートする場合、次のコールバック関数を提供します。
void GetForeignUpperPaths (PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel);
上位リレーション処理のための、ありうるアクセスパスを作成します。上位リレーションはプランナ用語で、ウィンドウ関数、ソート、テーブル更新など、全てのスキャン/結合後の問い合わせのことです。
この省略可能な関数は問い合わせのプラン作成時に呼ばれます。
今のところ、これは問い合わせに含まれる全てのベースリレーションが同じFDWに属する場合だけ呼ばれます。
この関数では、FDWがどのようにリモートで実行するか分かっている全てのスキャン/結合後の処理にForeignPath
パスを生成し、それらパスを指定された上位リレーションに加えるためにadd_path
を呼び出してください。
GetForeignJoinPaths
の時と同様に、この関数が何らかのパス作成に成功する必要はありません。なぜなら、ローカル処理を含んでいるパスはいつでも可能だからです。
stage
パラメータはどのスキャン/結合後の処理が現在考慮されているかを定めます。
output_rel
は本処理の計算方法をあらわすパスを受け取るであろう上位リレーションで、input_rel
は本処理への入力をあらわすリレーションです。
(注意:これらの処理は外部で実行されると考えられるため、output_rel
に加えられるForeignPath
パスは、一般的にinput_rel
のパスへの直接の依存を全く持たないでしょう。
しかしながら、手前の処理段階のために以前に生成されたパスを検査することは、冗長なプラン作成活動を回避するのに役立ちます。)
追加情報については55.4. 外部データラッパのクエリプラン作成を参照してください。
もしFDWが更新可能な外部テーブルをサポートする場合、FDWのニーズと能力に応じて、以下のコールバック関数の一部または全てを提供する必要があります。
void AddForeignUpdateTargets (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation);
UPDATE
とDELETE
の操作は、テーブルスキャン関数によって事前にフェッチされた行に対して実行されます。
FDWは、更新や削除の対象行を厳密に識別できるように行IDや主キー列の値といった追加情報を必要とするかもしれません。
それをサポートするために、この関数はUPDATE
やDELETE
の間に外部テーブルから取得される列のリストに追加の隠された(または「ジャンクの」)ターゲット列を追加することができます。
これを実行するには、フェッチする追加の値の式を含むTargetEntry
エントリをparsetree->targetList
に追加します。
それぞれのエントリはresjunk
= true
とマークされなければならず、また実行時にエントリを識別できる異なるresname
を持つ必要があります。
コアシステムがそのような名前のジャンク列を生成できるように、ctid
やN
wholerow
、wholerow
と一致する名前は使用しないでください。
N
この関数はプランナでなくリライタで呼ばれるので、利用可能な情報はプラン生成ルーチンで利用可能なものとは少し異なります。
parsetree
はUPDATE
やDELETE
コマンドのパース木で、target_rte
とtarget_relation
は対象の外部テーブルを表します。
もしAddForeignUpdateTargets
ポインタがNULL
に設定されている場合は、追加のターゲット式は追加されません。
(FDWが行を識別するのに不変の主キーに依存するのであればUPDATE
は依然として実現可能かもしれませんが、DELETE
操作を実装することは不可能になるでしょう。)
List * PlanForeignModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index);
外部テーブルに対する挿入、更新、削除に必要となる、追加のプラン生成アクションを実行します。
この関数は、更新処理を実行するModifyTable
プランノードに追加されるFDW固有の情報を生成します。この固有情報はList
形式でなければならず、また実行段階の間にBeginForeignModify
に渡されます。
root
はそのクエリに関するプランナのグローバル情報です。
plan
はfdwPrivLists
フィールドを除いて完成しているModifyTable
プランノードです。
resultRelation
は対象の外部テーブルをレンジテーブルの添字で識別します。
subplan_index
はModifyTable
プランノードの対象がどれであるかを0始まりで識別します。この情報はplan->plans
などのplan
の下位構造を指定したい場合に使用してください。
追加情報は55.4. 外部データラッパのクエリプラン作成を参照してください。
もしAddForeignUpdateTargets
ポインタがNULL
に設定されている場合は、追加のプラン作成時処理は実行されず、BeginForeignModify
に渡されるfdw_private
リストはNILになります。
void BeginForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, int eflags);
外部テーブルへの変更操作の実行を開始します。
このルーチンはエグゼキュータの起動中に呼び出されます。
実際のテーブル変更に先立って必要なあらゆる初期化処理を実行する必要があります。
その後、各タプルが挿入、更新、削除されるようにExecForeignInsert
、ExecForeignUpdate
、ExecForeignDelete
のいずれかが呼ばれます。
mtstate
は実行されているModifyTable
プランノード全体の状態です。プランに関する全般的なデータと実行状態はこの構造体経由で利用可能です。
rinfo
は対象の外部テーブルを表すResultRelInfo
構造体です。(ResultRelInfo
のri_FdwState
フィールドはこの操作で必要となる固有の状態をFDWが格納するのに利用できます。)
fdw_private
はもしあればPlanForeignModify
で生成された固有データを含みます。
eflags
は、このプランノードに関するエグゼキュータの操作モードを表すフラグビットを含みます。
(eflags & EXEC_FLAG_EXPLAIN_ONLY)
が真の場合、この関数は外部に見える処理を実行すべきではないことに注意してください。
ExplainForeignModify
やEndForeignModify
用にノード状態を有効するのに必要な最小限のことだけを実行するべきです。
もしBeginForeignModify
ポインタがNULL
に設定されている場合は、エグゼキュータ起動時には追加処理は何も実行されません。
TupleTableSlot * ExecForeignInsert (EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
外部テーブルにタプルを一つ挿入します。
estate
はそのクエリのグローバルな実行状態です。
rinfo
は対象の外部テーブルを表すResultRelInfo
構造体です。
slot
には挿入されるタプルが含まれます。その行型定義は外部テーブルと一致します。
planSlot
にはModifyTable
プランノードのサブプランが生成したタプルが含まれます。追加の「ジャンク」列を含みうる点において、slot
とは異なります。(planSlot
は一般的にINSERT
のケースにおいてはそれほど意味を持ちませんが、完全さのために提供されます。)
戻り値は実際に挿入されたデータ(例えばトリガー処理の結果などにより、提供されたデータとは異なるかもしれません)を含むスロットか、または(こちらも一般的にトリガーの結果)実際には挿入されなかった場合はNULLです。
渡されたslot
はこの用途に再利用可能です。
返却されたスロット内のデータはINSERT
クエリがRETURNING
句を持っていた場合もしくは外部テーブルがAFTER ROW
トリガを持っていた場合にのみ使われます。
トリガは全列を必要としますが、FDWはRETURNING
句の内容に応じて返却する列を一部にするかすべてにするかを最適化する余地があります。
それとは関係なく、処理成功を表すためになんらかのスロットは返却しなければなりません。さもないと、報告されるクエリの結果行数が誤った値になってしまいます。
もしExecForeignInsert
ポインタがNULL
に設定されている場合は、外部テーブルへの挿入の試みはエラーメッセージとともに失敗します。
TupleTableSlot * ExecForeignUpdate (EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
外部テーブル内のタプルを一つ更新します。
estate
はそのクエリのグローバルな実行状態です。
rinfo
は対象の外部テーブルを表すResultRelInfo
構造体です。
slot
にはタプルの新しいデータが含まれます。その行型定義は外部テーブルと一致します。
planSlot
にはModifyTable
プランノードのサブプランが生成したタプルが含まれます。追加の「ジャンク」列を含みうる点において、slot
とは異なります。実際、AddForeignUpdateTargets
が要求するジャンク列はこのスロットから利用可能です。
戻り値は実際に更新されたデータ(例えばトリガー処理の結果などにより、提供されたデータとは異なるかもしれません)を含むスロットか、または(こちらも一般的にトリガーの結果)実際には更新されなかった場合はNULLです。
渡されたslot
はこの用途に再利用可能です。
返却されたスロット内のデータはUPDATE
クエリがRETURNING
句を持っていた場合もしくは外部テーブルがAFTER ROW
トリガを持っていた場合にのみ使われます。
トリガは全列を必要としますが、FDWはRETURNING
句の内容に応じて返却する列を一部にするか全てにするかを最適化する余地があります。
それとは関係なく、処理成功を表すためになんらかのスロットは返却しなければなりません。さもないと、報告されるクエリの結果行数が誤った値になってしまいます。
もしExecForeignUpdate
ポインタがNULL
に設定されている場合は、外部テーブルへの更新の試みはエラーメッセージとともに失敗します。
TupleTableSlot * ExecForeignDelete (EState *estate, ResultRelInfo *rinfo, TupleTableSlot *slot, TupleTableSlot *planSlot);
外部テーブルからタプルを一つ削除します。
estate
はそのクエリのグローバルな実行状態です。
rinfo
は対象の外部テーブルを表すResultRelInfo
構造体です。
slot
にはタプルの新しいデータが含まれます。その行型定義は外部テーブルと一致します。
planSlot
にはModifyTable
プランノードのサブプランが生成したタプルが含まれます。実際、AddForeignUpdateTargets
が要求するジャンク列はこのスロットが運びます。ジャンク列は削除されるタプルを識別するために使用しなければなりません。
戻り値は実際に削除されたデータを含むスロットか、または(一般的にトリガーの結果)実際には削除されなかった場合はNULLです。
渡されたslot
は返却するタプルを保持する用途に利用可能です。
返却されたスロット内のデータはDELETE
クエリがRETURNING
句を持っていた場合もしくは外部テーブルがAFTER ROW
トリガを持っていた場合にのみ使われます。
トリガは全列を必要としますが、FDWはRETURNING
句の内容に応じて返却する列を一部にするか全てにするかを最適化する余地があります。
それとは関係なく、処理成功を表すためになんらかのスロットは返却しなければなりません。さもないと、報告されるクエリの結果行数が誤った値になってしまいます。
もしExecForeignDelete
ポインタがNULL
に設定されている場合は、外部テーブルからの削除の試みはエラーメッセージとともに失敗します。
void EndForeignModify (EState *estate, ResultRelInfo *rinfo);
テーブル更新を終えてリソースを解放します。pallocされたメモリの解放は通常重要ではありませんが、開いたファイルやリモートサーバへの接続はクリーンアップするべきです。
もしEndForeignModify
ポインタがNULL
に設定されている場合は、エグゼキュータ終了時には追加処理は何も実行されません。
int IsForeignRelUpdatable (Relation rel);
指定された外部テーブルがどの更新処理をサポートしているかを報告します。
戻り値は、その外部テーブルがサポートする操作を表すルールイベント番号のビットマスクである必要があります。
UPDATE
用の(1 << CMD_UPDATE) = 4
、INSERT
用の(1 << CMD_INSERT) = 8
、DELETE
用の(1 << CMD_DELETE) = 16
といったCmdType
列挙値を使います。
もしIsForeignRelUpdatable
ポインタがNULL
に設定されていると、外部テーブルはExecForeignInsert
、ExecForeignUpdate
、ExecForeignDelete
を提供していると、それぞれ挿入、更新、削除をサポートしていると判断します。
この関数は、FDWが一部のテーブルについてのみ更新をサポートする場合にのみ必要です。
(そのような場合でも、この関数でチェックする代わりにクエリ実行関数でエラーにしても構いません。しかしながら、この関数はinformation_schema
のビューの表示で更新可否を判定するのに使用されます。)
外部テーブルへの挿入、更新、削除は、代替インターフェイス一式を実装することで最適化できます。 通常の挿入、更新、削除のインターフェイスは行をリモートサーバから取得し、その後、それらの行を一つずつ変更します。 一部の場合にはこの一行ごとのやり方は必要ですが、非効率とも言えます。 外部サーバについて行が本当はそれらを引き出すことなしに変更されるべきと判断できて、操作に影響を与えるローカルトリガーが無いならば、操作全体がリモートサーバで実行されるように計画することができます。 以下に示すインターフェイスはこれを可能にします。
bool PlanDirectModify (PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index);
リモートサーバ上で直接変更を実行することが安全かを判断します。
そうであれば、そのために必要なプラン作成の動作を実行した後にtrue
を返します。
さもなくば、false
を返します。
この省略可能な関数は問い合わせのプラン作成時に呼ばれます。
この関数が成功すると、BeginDirectModify
、IterateDirectModify
、EndDirectModify
が実行段階で代わりに呼び出されます。
成功しなければ、テーブルの変更は前述のテーブル更新関数を使って実行されます。
パラメータはPlanForeignModify
に対するものと同じです。
リモートサーバで直接変更を実行するには、本関数は対象サブプランをリモートサーバ上で直接変更するForeignScan
プランノードで書き換えしなければなりません。
ForeignScan
のoperation
フィールドにはCmdType
列挙値を適切に、すなわち、UPDATE
にはCMD_UPDATE
、INSERT
にはCMD_INSERT
、DELETE
にはCMD_DELETE
を設定しなければいけません。
追加情報は55.4. 外部データラッパのクエリプラン作成を参照してください。
PlanDirectModify
ポインタにNULL
が設定されている場合、
リモートサーバでの直接変更の実行は試みられません。
void BeginDirectModify (ForeignScanState *node, int eflags);
リモートサーバでの直接変更を実行する準備をします。
この関数はエグゼキュータが開始するときに呼び出されます。
この関数は(最初のIterateDirectModify
呼び出しで実行されるであろう)直接変更より前に必要とされる全ての初期化を実行するべきです。
ForeignScanState
ノードはすでに作られていますが、fdw_state
がフィールドはまだNULLです。
変更するテーブルに関する情報はForeignScanState
ノードを通して(具体的にはPlanDirectModify
で提供されるFDWプライベート情報を含む、元となるForeignScan
プランノードから)入手可能です。
eflags
は、このプランノードに関するエグゼキュータの操作モードを表すフラグビットを含みます。
(eflags & EXEC_FLAG_EXPLAIN_ONLY)
が真の場合、この関数は外部に見える処理を実行すべきではないことに注意してください。
ExplainDirectModify
やEndDirectModify
用にノード状態を有効するのに必要な最小限のことだけを実行するべきです。
BeginDirectModify
ポインタがNULL
に設定されている場合、リモートサーバでの直接変更の実行は試みられません。
TupleTableSlot * IterateDirectModify (ForeignScanState *node);
INSERT
、UPDATE
、または、DELETE
の問い合わせがRETURNING
句を持たないときには、リモートサーバでの直接変更の後、単にNULLが返ります。
問い合わせがRETURNING
句を持つときには、RETURNING
計算に必要なデータを含む結果を一つ取り出し、タプルテーブルスロットでそれを返します(この用途にはノードのScanTupleSlot
を使うべきです)。
実際に挿入、更新、削除されたデータはノードのEState
のes_result_relation_info->ri_projectReturning->pi_exprContext->ecxt_scantuple
に格納されなければなりません。
有効な行がそれ以上なければNULLを返します。
これは呼び出しの間でリセットされる寿命の短いメモリコンテキストで呼び出されることに注意してください。
より長命な格納場所を必要とするなら、BeginDirectModify
でメモリコンテキストを作るか、ノードのEState
のes_query_cxt
を使ってください。
返される行は、ターゲットリストfdw_scan_tlist
が提供されたなら、それとマッチしなければならず、提供されていない場合は変更されている外部テーブルの行型とマッチしなければなりません。
RETURNING
計算に不要な列を取り出さないように最適化することを選ぶなら、それらの列の位置にNULLを入れるか、あるいはそれらの列を除いたfdw_scan_tlist
リストを生成するべきです。
問い合わせがRETURNING
句をもつかどうかによらず、問い合わせが報告する行数はFDW自身によって増加されなければなりません。
問い合わせがRETURNING
句を持たないときも、FDWはEXPLAIN ANALYZE
の場合のForeignScanState
nodeむけに行数を増加させなければなりません。
IterateDirectModify
ポインタがNULL
に設定されている場合、リモートサーバでの直接変更の実行は試みられません。
void EndDirectModify (ForeignScanState *node);
リモートサーバでの直接変更の後、クリーンアップします。 pallocされたメモリの解放は通常重要ではありませんが、開いたファイルやリモートサーバへの接続はクリーンアップするべきです。
EndDirectModify
ポインタがNULL
に設定されている場合、リモートサーバでの直接変更の実行は試みられません。
FDWが(55.5. 外部データラッパでの行ロックで説明される)遅延行ロックをサポートする場合は、以下のコールバック関数を提供する必要があります。
RowMarkType GetForeignRowMarkType (RangeTblEntry *rte, LockClauseStrength strength);
行の印付けでどのオプションを外部テーブルに使うかを報告します。
rte
はテーブルのRangeTblEntry
ノードで、strength
は関連するFOR UPDATE/SHARE
句があれば、それが要求するロックの強さを表します。
その結果は、RowMarkType
列挙型のメンバーでなければなりません。
この関数はUPDATE
、DELETE
、SELECT FOR UPDATE/SHARE
の問い合わせに現れ、かつUPDATE
あるいはDELETE
の対象ではない各外部テーブルについて、問い合わせの計画時に呼び出されます。
GetForeignRowMarkType
のポインタがNULL
に設定されていると、必ずROW_MARK_COPY
オプションが使われます。
(これはRefetchForeignRow
が決して呼び出されないので、それを提供する必要もない、ということを意味します。)
さらなる情報については55.5. 外部データラッパでの行ロックを参照してください。
HeapTuple RefetchForeignRow (EState *estate, ExecRowMark *erm, Datum rowid, bool *updated);
必要ならロックした後で、外部テーブルから1つのタプルを再フェッチします。
estate
は問い合わせのグローバルな実行状態です。
erm
は対象の外部テーブルおよび獲得する行ロックの種別(あれば)を記述するExecRowMark
構造体です。
rowid
はフェッチするタプルを特定するものです。
updated
は出力パラメータです。
この関数は、フェッチしたタプルをpallocして複製したものを返すか、あるいは行ロックが取得できなければNULL
を返します。
獲得する行ロックの種別はerm->markType
で指定されますが、この値は事前にGetForeignRowMarkType
から返されたものです。
(ROW_MARK_REFERENCE
は行のロックを獲得せずに、単にタプルを再フェッチすることを意味し、また、ROW_MARK_COPY
はこのルーチンで使われることはありません。)
そして、*updated
はフェッチしたタプルが、以前に取得したものと同じではなく、更新されたバージョンであったときにtrue
にセットされます。
(どちらなのかFDWが判断できない場合は、true
を返すことが推奨されます)。
デフォルトでは、行ロックの獲得に失敗したときはエラーを発生させるべきであることに注意してください。
NULL
を返すのが適切なのは、erm->waitPolicy
でSKIP LOCKED
オプションが指定されている場合だけです。
rowid
は、再フェッチする行を以前読んだ時のctid
値です。
rowid
値はDatum
として渡されますが、現在はtid
にしかなりません。
将来は行ID以外のデータ型が可能になることを期待して、関数APIとすることが選択されました。
RefetchForeignRow
ポインタがNULL
の場合、行を再フェッチする試みは失敗し、エラーメッセージを発行します。
さらなる情報については55.5. 外部データラッパでの行ロックを参照してください。
bool RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot);
以前に戻されたタプルが、関連するスキャンおよび結合の制約とまだ一致しているか再検査し、更新されたバージョンのタプルを提供する場合もあります。
結合のプッシュダウンを行わない外部データラッパでは、通常は、これをNULL
にセットし、代わりにfdw_recheck_quals
を適切にセットする方が便利でしょう。
しかし、外部結合をプッシュダウンする場合、すべてのベーステーブルに関する検査を結果のタプルに適用するだけでは、たとえすべての必要な属性がそこにあったとしても十分ではありません。
なぜなら一部の制約が一致しないことで、タプルが戻されない代わりに、一部の属性がNULLになってしまうかもしれないからです。
RecheckForeignScan
制約を再検査し、それが依然として満たされていれば真を、そうでなければ偽を返すことができます。
それだけでなく、置換されたタプルを提供されたスロットに格納することもできます。
結合のプッシュダウンを実装する場合、外部データラッパは通常、再検査のためだけに使用される代替のローカル結合プランを構築します。
これがForeignScan
の外部サブプランとなります。
再検査が必要な時は、このサブプランを実行して、結果のタプルをスロットに格納することができます。
どのベーステーブルも最大で1行しか返さないので、このプランは効率的である必要はありません。
例えば、すべての結合をネステッドループで実装することもできます。
関数GetExistingLocalJoinPath
は、存在するパスから代替ローカルの結合プランとして使用可能な適当なローカル結合パスを検索するのに使われるかもしれません。
GetExistingLocalJoinPath
は指定された結合リレーションのパスリストのパラメータ化されていないパスを検索します。
(そのようなパスが見つからなかった場合はNULLを返します。この場合、外部データラッパはそれ自身によりローカルパスを構築するかもしれず、あるいは、その結合むけのアクセスパスを作らないことを選択するかもしれません。)
EXPLAIN
のためのFDWルーチンvoid ExplainForeignScan (ForeignScanState *node, ExplainState *es);
外部テーブルスキャンの追加のEXPLAIN
出力を表示します。
EXPLAIN
出力にフィールドを追加するためにExplainPropertyText
や関連する関数を呼び出すことができます。
es
の中のフラグフィールドは何を表示するかを決めるのに使用できます。また、EXPLAIN ANALYZE
の場合には、実行時統計情報を提供するためにForeignScanState
ノードの状態を調べることができます。
もしExplainForeignScan
ポインタがNULL
に設定されている場合は、EXPLAIN
中に追加情報は表示されません。
void ExplainForeignModify (ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, struct ExplainState *es);
外部テーブル更新の追加のEXPLAIN
出力を表示します。
EXPLAIN
出力にフィールドを追加するためにExplainPropertyText
や関連する関数を呼び出すことができます。
es
の中のフラグフィールドは何を表示するかを決めるのに使用できます。また、EXPLAIN ANALYZE
の場合には、実行時統計情報を提供するためにForeignScanState
ノードの状態を調べることができます。
最初の4つの引数はBeginForeignModify
と同じです。
もしExplainForeignModify
ポインタがNULL
に設定されている場合は、EXPLAIN
中に追加情報は表示されません。
void ExplainDirectModify (ForeignScanState *node, ExplainState *es);
リモートサーバでの直接変更について追加EXPLAIN
出力を表示します。
この関数はEXPLAIN
出力にフィールドを加えるためにExplainPropertyText
と関連の関数を呼ぶことができます。
es
の中のフラグフィールドは何を表示するかを決めるのに使用できます。また、EXPLAIN ANALYZE
の場合には、実行時統計情報を提供するためにForeignScanState
ノードの状態を調べることができます。
ExplainForeignScan
ポインタがNULL
に設定されている場合は、EXPLAIN
中に追加情報は表示されません。
ANALYZE
のためのFDWルーチンbool AnalyzeForeignTable (Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages);
この関数はANALYZEが外部テーブルに対して実行されたときに呼び出されます。
もしFDWがこの外部テーブルの統計情報を収集できる場合は、そのテーブルからサンプル行を集める関数のポインタとページ単位でのテーブルサイズの見積もりをそれぞれfunc
とtotalpages
に渡しtrue
を返す必要があります。
そうでない場合は、false
を返します。
もしFDWが統計情報の収集をどのテーブルについてもサポートしない場合は、AnalyzeForeignTable
ポインタをNULLにすることもできます。
もし提供される場合は、サンプル収集関数はこのようなシグネチャを持つ必要があります。
int AcquireSampleRowsFunc (Relation relation, int elevel, HeapTuple *rows, int targrows, double *totalrows, double *totaldeadrows);
最大targrows
行のランダムサンプルをテーブルから収集し、呼び出し元が提供するrows
配列に格納する必要があります。
実際に収集された行の数を返す必要があります。
さらに、テーブルに含まれる有効行と不要行の合計数の見積もりを出力パラメータのtotalrows
とtotaldeadrows
に返す必要があります。(もしFDWが不要行という概念を持たない場合はtotaldeadrows
を0に設定してください。)
IMPORT FOREIGN SCHEMA
のためのFDWルーチンList * ImportForeignSchema (ImportForeignSchemaStmt *stmt, Oid serverOid);
外部テーブル作成コマンドのリストを取得します。 この関数はIMPORT FOREIGN SCHEMAを実行する時に呼び出され、その文の解析木と外部サーバが使用するOIDとを渡されます。 C文字列のリストを返し、その各文字列はCREATE FOREIGN TABLEコマンドを含んでいる必要があります。 これらの文字列はコアサーバが解析して実行します。
ImportForeignSchemaStmt
構造体において、remote_schema
はリモートスキーマの名前で、そこからテーブルがインポートされます。
list_type
はテーブル名のフィルタ方法を指定します。
ここで、FDW_IMPORT_SCHEMA_ALL
はリモートスキーマのすべてのテーブルをインポートすること(この場合、table_list
は空にします)、
FDW_IMPORT_SCHEMA_LIMIT_TO
はtable_list
に列挙されたテーブルだけを含めること、
そしてFDW_IMPORT_SCHEMA_EXCEPT
はtable_list
に列挙されたテーブルを除外することを意味します。
options
はインポートのプロセスで使用されるオプションのリストです。
オプションの意味はFDWに依存します。
例えば、FDWは列のNOT NULL
属性をインポートするかどうかを定めるオプションを使うことができます。
これらのオプションはFDWがデータベースオブジェクトのオプションとしてサポートするものと何ら関係ある必要はありません。
FDWはImportForeignSchemaStmt
のlocal_schema
フィールドを無視しても良いです。
なぜなら、コアサーバは解析されたCREATE FOREIGN TABLE
コマンドにその名前を自動的に挿入するからです。
FDWはlist_type
およびtable_list
で指定されるフィルタの実装にも注意する必要はありません。
なぜなら、コアサーバはそれらのオプションによって除外されるテーブルに対して戻されたコマンドをすべて自動的にスキップするからです。
しかし、除外されるテーブルについてコマンドを作成する作業を回避するのは、そもそも役立つことが多いです。
関数IsImportableForeignTable()
は指定の外部テーブル名がフィルタを通るかどうかの検査に役立つかもしれません。
FDWがテーブル定義のインポートをサポートしない場合は、ImportForeignSchema
ポインタをNULL
にセットすることができます。
ForeignScan
ノードは、オプションとして、パラレル実行をサポートします。
並列ForeignScan
は複数プロセスで実行され、全ての協調プロセスにわたって各行が一度だけ返るべきです。
これを行うため、プロセスは動的共有メモリの固定サイズチャンクを通して調整をはかることができます。
この共有メモリは全プロセスで同じアドレスに割り当てされることが保証されませんので、ポインタはおそらく使えません。
以下のコールバックは一般に全て省略可能ですが、パラレル実行をサポートするためには必要です。
bool IsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte);
スキャンがパラレルワーカーで実行できるかテストします。 この関数はプランナが並列プランが可能であろうと考えるときだけ呼ばれます。また、そのスキャンにとってパラレルワーカーで実行するのが安全であるとき真を返すべきです。 リモートデータソースがトランザクションのセマンティクスを持つ場合は、一般にあてはまりません。ただし、ワーカーのデータへの接続を何らかの形でリーダーとして同じトランザクション文脈を共有させることができる場合を除きます。
このコールバックが定義されていない場合、スキャンはパラレルリーダー内で実行しなければならないと想定されます。 真を返すことは、スキャンがパラレルワーカーで実行可能であるだけで、パラレルに実行可能であることを意味するのでは無いことに注意してください。 そのため、この関数を定義することはパラレル実行がサポートされていないときでも役立つ可能性があります。
Size EstimateDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt);
並列操作に必要とされるであろう動的共有メモリ量を推定します。 これは実際に使われる量よりも大きくてよいですが、小さくてはいけません。 戻り値はバイト単位です。
void InitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt, void *coordinate);
並列処理で必要とされる動的共有メモリを初期化します。
coordinate
はEstimateDSMForeignScan
の戻り値に等しいサイズだけ割り当てられた領域へのポインタです。
void InitializeWorkerForeignScan(ForeignScanState *node, shm_toc *toc, void *coordinate);
InitializeDSMForeignScan
でリーダーがセットアップした共有状態に基づくパラレルワーカーのカスタム状態を初期化します。
このコールバックは省略可能で、このカスタムパスがパラレル実行をサポートする場合だけ用意する必要があります。