他のバージョンの文書 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

53.2. 外部データラッパのコールバックルーチン

FDWハンドラ関数は、以下で説明するコールバック関数へのポインタを含む、pallocされたFdwRoutine構造体を返します。 スキャンに関連した関数は必須で、それ以外は省略可能です。

FdwRoutine構造体はsrc/include/foreign/fdwapi.hで宣言されていますので、追加情報はそちらを参照してください。

53.2.1. 外部テーブルスキャンのためのFDWルーチン

void
GetForeignRelSize (PlannerInfo *root,
                   RelOptInfo *baserel,
                   Oid foreigntableid);

外部テーブルのリレーションサイズ見積もりを取得します。この関数は、ある外部テーブルをスキャンするクエリのプラン作成の開始時に呼び出されます。rootはそのクエリに関するプランナのグローバル情報です。baserelはそのテーブルに関するプランナの情報です。そして、foreigntableidはその外部テーブルのpg_class OIDです。(foreigntableidはプランナデータ構造体からも取得できますが、手間を省くために明示的に渡されます。)

この関数は、制約条件によるフィルタリングも考慮に入れた、そのテーブルスキャンが返すと見込まれる件数にbaserel->rowsを更新するべきです。 baserel->rowsの初期値は固定のデフォルト見積もりなので、可能な限り置き換えられるべきです。この関数は、行の幅のよりよい見積もりを計算できるのであれば、baserel->widthを更新することも選択出来ます。

追加情報については項53.4を参照してください。

void
GetForeignPaths (PlannerInfo *root,
                 RelOptInfo *baserel,
                 Oid foreigntableid);

外部テーブル対するスキャンとしてありえるアクセスパスを作成します。 この関数はクエリのプラン作成中に呼び出されます。 引数は、先に呼ばれているGetForeignRelSizeと同じです。

この関数は、少なくとも一つのアクセスパス(ForeignPathノード)を作成して、それぞれのパスをbaserel->pathlistに追加するためにadd_pathを呼ばなければなりません。 ForeignPathノードを構築するにはcreate_foreignscan_pathを使うことが推奨されています。 この関数は、たとえばソート済みの結果を表現する有効なpathkeysを持つパスのような複数のアクセスパスを作成することが出来ます。 それぞれのアクセスパスはコスト見積もりを含まねばならず、また意図した特定のスキャン方式を識別するのに必要なFDW固有の情報を持つことが出来ます。

追加情報については項53.4を参照してください。

ForeignScan *
GetForeignPlan (PlannerInfo *root,
                RelOptInfo *baserel,
                Oid foreigntableid,
                ForeignPath *best_path,
                List *tlist,
                List *scan_clauses);

選択された外部アクセスパスからForeignScanプランノードを作成します。 この関数はクエリプラン作成の最後に呼び出されます。 引数は、GetForeignRelSizeと同じものに、選択されたForeignPath(事前にGetForeignPathsによって作成されたもの)と、そのプランノードによって出力されるターゲットリスト、およびそのプランノードで強制される条件句が追加されます。

この関数はForeignScanプランノードを作成して返さなければなりません。ForeignScanノードを構築するにはmake_foreignscanを使うことが推奨されています。

追加情報については項53.4を参照してください。

void
BeginForeignScan (ForeignScanState *node,
                  int eflags);

外部テーブルスキャンの実行を開始します。 この関数はエグゼキュータの起動中に呼び出されます。 スキャンを開始できるようになる前に、あらゆる必要な初期化を実行するべきですが、実際のスキャンの実行を始めるべきではありません(それは最初のIterateForeignScan呼び出しにおいて行われるべきです)。 ForeignScanStateノードは作成されていますが、そのfdw_stateフィールドはNULLのままです。 スキャンするテーブルの情報は、ForeignScanStateノード(実際にはその先にある、PlanForeignScanから返されたFdwPlan構造体へのポインタを含む、ForeignScanプランノード)を通じてアクセス可能です。 eflagsは、このプランノードに関するエグゼキュータの操作モードを表すフラグビットを含みます。

(eflags & EXEC_FLAG_EXPLAIN_ONLY)が真の場合、この関数は外部に見える処理を実行すべきではないことに注意してください。 ExplainForeignScanEndForeignScan用にノード状態を有効にするのに必要とされる最小限のことだけをすべきです。

TupleTableSlot *
IterateForeignScan (ForeignScanState *node);

外部ソースから一行を取り出して、それをタプルテーブルスロットに入れて返します(この用途にはnodeのScanTupleSlotを使うべきです)。 利用可能な行がない場合は、NULLを返します。 タプルテーブルスロット機構を使うと、物理タプルと仮想タプルのどちらでも返せます。 ほとんどの場合、パフォーマンスの観点から後者を選ぶのが良いでしょう。 この関数は、呼出しごとにリセットされる短命なメモリコンテキスト内で呼び出されることに注意してください。 より長命なストレージが必要な場合は、BeginForeignScanでメモリコンテキストを作成するか、ノードのEStateに含まれるes_query_cxtを使用してください。

返される行はスキャンされている外部テーブルのカラムシグネチャと一致しなければなりません。 不要なカラムを取り出さないように最適化することを選ぶなら、それらのカラム位置にNULLを入れるべきです。

PostgreSQLのエグゼキュータは返された行が外部テーブルの列に定義されたNOT NULL制約に違反しているかどうかは気にしません。しかし、プランナはそれに着目し、NULL値を含まないと宣言された列にNULL値が現れた場合に不正なクエリ最適化をしてしまう場合があることに注意してください。 ユーザがあってはならないと宣言したのにNULL値に遭遇した場合は(データ型が一致しなかった場合にする必要があるのと同様に)エラーを発生させるのが適切でしょう。

void
ReScanForeignScan (ForeignScanState *node);

先頭からスキャンを再開します。 スキャンが依存するいずれかのパラメータが値を変更しているかもしれないので、新しいスキャンが必ずしも厳密に同じ行を返すとは限らないことに注意してください。

void
EndForeignScan (ForeignScanState *node);

スキャンを終了しリソースを解放します。 通常、pallocされたメモリを解放することは重要ではありませんが、開いたファイルやリモートサーバへの接続などはクリーンアップするべきです。

53.2.2. 外部テーブル更新のためのFDWルーチン

もしFDWが更新可能な外部テーブルをサポートする場合、FDWのニーズと能力に応じて、以下のコールバック関数の一部または全てを提供する必要があります。

void
AddForeignUpdateTargets (Query *parsetree,
                         RangeTblEntry *target_rte,
                         Relation target_relation);

UPDATEDELETEの操作は、テーブルスキャン関数によって事前にフェッチされた行に対して実行されます。 FDWは、更新や削除の対象行を厳密に識別できるように行IDや主キー列の値といった追加情報を必要とするかもしれません。 それをサポートするために、この関数はUPDATEDELETEの間に外部テーブルから取得される列のリストに追加の隠された(または"ジャンクの")ターゲット列を追加することができます。

これを実行するには、フェッチする追加の値の式を含むTargetEntryエントリをparsetree->targetListに追加します。 それぞれのエントリはresjunk = trueとマークされなければならず、また実行時にエントリを識別できる異なるresnameを持つ必要があります。 コアシステムがそのような名前のジャンク列を生成できるように、ctidNwholerowwholerowNと一致する名前は使用しないでください。

この関数はプランナでなくリライタで呼ばれるので、利用可能な情報はプラン生成ルーチンで利用可能なものとは少し異なります。 parsetreeUPDATEDELETEコマンドのパース木で、target_rtetarget_relationは対象の外部テーブルを表します。

もしAddForeignUpdateTargetsポインターがNULLに設定されている場合は、追加のターゲット式は追加されません。 (FDWが行を識別するのに不変の主キーに依存するのであればUPDATEは依然として実現可能かもしれませんが、DELETE操作を実装することは不可能になるでしょう。)

List *
PlanForeignModify (PlannerInfo *root,
                   ModifyTable *plan,
                   Index resultRelation,
                   int subplan_index);

外部テーブルに対する挿入、更新、削除に必要となる、追加のプラン生成アクションを実行します。 この関数は、更新処理を実行するModifyTableプランノードに追加されるFDW固有の情報を生成します。この固有情報はList形式でなければならず、また実行段階の間にBeginForeignModifyに渡されます。

rootはそのクエリに関するプランナのグローバル情報です。 planfdwPrivListsフィールドを除いて完成しているModifyTableプランノードです。 resultRelationは対象の外部テーブルをレンジテーブルの添字で識別します。 subplan_indexModifyTableプランノードの対象がどれであるかを0始まりで識別します。この情報はplan->plansなどのplanの下位構造を指定したい場合に使用してください。

追加情報は項53.4を参照してください。

もしAddForeignUpdateTargetsポインターがNULLに設定されている場合は、追加のプラン作成時処理は実行されず、BeginForeignModifyに渡されるfdw_privateリストはNILになります。

void
BeginForeignModify (ModifyTableState *mtstate,
                    ResultRelInfo *rinfo,
                    List *fdw_private,
                    int subplan_index,
                    int eflags);

外部テーブルへの変更操作の実行を開始します。 このルーチンはエグゼキュータの起動中に呼び出されます。 実際のテーブル変更に先立って必要なあらゆる初期化処理を実行する必要があります。 その後、各タプルが挿入、更新、削除されるようにExecForeignInsertExecForeignUpdateExecForeignDeleteのいずれかが呼ばれます。

mtstateは実行されているModifyTableプランノード全体の状態です。プランに関する全般的なデータと実行状態はこの構造体経由で利用可能です。 rinfoは対象の外部テーブルを表すResultRelInfo構造体です。(ResultRelInfori_FdwStateフィールドはこの操作で必要となる固有の状態をFDWが格納するのに利用できます。) fdw_privateはもしあればPlanForeignModifyで生成された固有データを含みます。 eflagsは、このプランノードに関するエグゼキュータの操作モードを表すフラグビットを含みます。

(eflags & EXEC_FLAG_EXPLAIN_ONLY)が真の場合、この関数は外部に見える処理を実行すべきではないことに注意してください。 ExplainForeignModifyEndForeignModify用にノード状態を有効するのに必要な最小限のことだけを実行するべきです。

もし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) = 4INSERT用の(1 << CMD_INSERT) = 8DELETE用の(1 << CMD_DELETE) = 16といったCmdType列挙値を使います。

もしIsForeignRelUpdatableポインターがNULLに設定されていると、外部テーブルはExecForeignInsertExecForeignUpdateExecForeignDeleteを提供していると、それぞれ挿入、更新、削除をサポートしていると判断します。 この関数は、FDWが一部のテーブルについてのみ更新をサポートする場合にのみ必要です。 (そのような場合でも、この関数でチェックする代わりにクエリ実行関数でエラーにしても構いません。しかしながら、この関数はinformation_schemaのビューの表示で更新可否を判定するのに使用されます。)

53.2.3. 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中に追加情報は表示されません。

53.2.4. ANALYZEのためのFDWルーチン

bool
AnalyzeForeignTable (Relation relation,
                     AcquireSampleRowsFunc *func,
                     BlockNumber *totalpages);

この関数はANALYZEが外部テーブルに対して実行されたときに呼び出されます。 もしFDWがこの外部テーブルの統計情報を収集できる場合は、そのテーブルからサンプル行を集める関数のポインタとページ単位でのテーブルサイズの見積もりをそれぞれfunctotalpagesに渡しtrueを返す必要があります。 そうでない場合は、falseを返します。

もしFDWが統計情報の収集をどのテーブルについてもサポートしない場合は、AnalyzeForeignTableポインターをNULLにすることもできます。

もし提供される場合は、サンプル収集関数はこのようなシグネチャを持つ必要があります。

int
AcquireSampleRowsFunc (Relation relation, int elevel,
                       HeapTuple *rows, int targrows,
                       double *totalrows,
                       double *totaldeadrows);

最大targrows行のランダムサンプルをテーブルから収集し、呼び出し元が提供するrows配列に格納する必要があります。 実際に収集された行の数を返す必要があります。 さらに、テーブルに含まれる有効行と不要行の合計数の見積もりを出力パラメータのtotalrowstotaldeadrowsに返す必要があります。(もしFDWが不要行という概念を持たない場合はtotaldeadrowsを0に設定してください。)