PostgreSQL 9.4.5文書 | |||
---|---|---|---|
前のページ | 上に戻る | 第 53章外部データラッパの作成 | 次のページ |
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を更新することも選択出来ます。
追加情報については項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)が真の場合、この関数は外部に見える処理を実行すべきではないことに注意してください。
ExplainForeignScan
やEndForeignScan
用にノード状態を有効にするのに必要とされる最小限のことだけをすべきです。
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されたメモリを解放することは重要ではありませんが、開いたファイルやリモートサーバへの接続などはクリーンアップするべきです。
もしFDWが更新可能な外部テーブルをサポートする場合、FDWのニーズと能力に応じて、以下のコールバック関数の一部または全てを提供する必要があります。
void AddForeignUpdateTargets (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation);
UPDATEとDELETEの操作は、テーブルスキャン関数によって事前にフェッチされた行に対して実行されます。 FDWは、更新や削除の対象行を厳密に識別できるように行IDや主キー列の値といった追加情報を必要とするかもしれません。 それをサポートするために、この関数はUPDATEやDELETEの間に外部テーブルから取得される列のリストに追加の隠された(または"ジャンクの")ターゲット列を追加することができます。
これを実行するには、フェッチする追加の値の式を含むTargetEntryエントリをparsetree->targetListに追加します。 それぞれのエントリはresjunk = trueとマークされなければならず、また実行時にエントリを識別できる異なるresnameを持つ必要があります。 コアシステムがそのような名前のジャンク列を生成できるように、ctidNやwholerow、wholerowNと一致する名前は使用しないでください。
この関数はプランナでなくリライタで呼ばれるので、利用可能な情報はプラン生成ルーチンで利用可能なものとは少し異なります。 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の下位構造を指定したい場合に使用してください。
追加情報は項53.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のビューの表示で更新可否を判定するのに使用されます。)
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中に追加情報は表示されません。
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に設定してください。)