PostgreSQL 9.4.5文書 | |||
---|---|---|---|
前のページ | 上に戻る | 第 53章外部データラッパの作成 | 次のページ |
FDWコールバック関数のGetForeignRelSize
、GetForeignPaths
、GetForeignPlan
、PlanForeignModify
はPostgreSQLプランナの動作と協調しなければなりません。ここでは、これらの関数がすべき事に関するいくつかの注意事項を述べます。
rootとbaserelに含まれる情報は、外部テーブルから取得する必要のある情報の量(とそれによるコスト)を削減するために使用できます。 baserel->baserestrictinfoは、取得される行をフィルタリングする制約条件(WHERE句)を含んでいるため、特に興味深いものです。(コアのエグゼキュータが代わりにそれらをチェックできるので、FDW自身がこれらの制約を適用しなければならないわけではありません。) baserel->reltargetlistはどのカラムが取得される必要があるかを決定するのに使用できます。ただし、このリストはForeignScanプランノードから出力すべきカラムしか含んでおらず、条件検査には必要だがクエリからは出力されないカラムは含まないことに注意してください。
様々なプライベートフィールドがFDWのプラン作成関数で情報を格納する目的で利用できます。 一般的に、プラン作成の最後に回収できるように、FDW固有フィールドに格納するものは全てpallocで確保すべきです。
baserel->fdw_privateは、voidポインタで、FDWのプラン作成関数で特定の外部テーブルに関する情報を格納する目的で利用できます。
コアプランナは、baserelノードが作成されるときにNULLで初期化するときを除いて、このフィールドに一切に触れません。
このフィールドは、GetForeignRelSize
からGetForeignPaths
やGetForeignPaths
からGetForeignPlan
といったように情報を順次伝えるの便利で、結果として再計算を省くことができます。
GetForeignPaths
では、ForeignPathノードのfdw_privateフィールドに固有情報を格納することで、異なるアクセスパスを区別できます。fdw_privateはListポインタとして宣言されていますが、コアプランナがこのフィールドを操作することはないため、実際にはなんでも格納できます。
しかし、バックエンドのデバッグサポート機能を利用できるようにnodeToString
でダンプ出来る形式を使うのが最良の手法です。
GetForeignPlan
では、選択されたForeignPathノードのfdw_privateフィールドを調べて、ForeignScanプランノード内に格納されプラン実行時に利用可能なfdw_exprsとfdw_privateの二つのリストを生成することができます。
これらは両方ともcopyObject
がコピーできる形式でなければなりません。
fdw_privateリストにはこれ以外に制約はなく、コアバックエンドによって解釈されることはありません。
fdw_exprsリストがNILでない場合は、クエリ実行時に実行されることを意図した式ツリーが含まれていることが期待されます。
これらのツリーは、完全に実行可能な状態にするためにプランナによる後処理を受けます。
GetForeignPlan
では、一般的に渡されたターゲットリストはそのままプランノードにコピーできます。
渡されたscan_clausesリストはbaserel->baserestrictinfoと同じ句を含みますが、実行効率のよい別の順番に並べ替えることもできます。
FDWにできるのがRestrictInfoノードをscan_clausesリストから(extract_actual_clauses
を使って)抜き出して、全ての句をプランノードのqualリストに入れるだけ、といった単純なケースでは、全ての句は実行時にエグゼキュータによってチェックされます。
より複雑なFDWは内部で一部の句をチェックできるかもしれませんが、そのような場合には、エグゼキュータが再チェックのために時間を無駄にしないように、それらの句はプランノードのqualリストから削除できます。
たとえば、ローカル側で評価された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のようなものを伴うでしょう。
FDWはそのテーブルの条件句のみに依存するパスを常に少なくとも一つは生成すべきです。結合クエリでは、例えばforeign_variable = local_variableといった結合句に依存するパス(群)を生成することもできます。
そのような句はbaserel->baserestrictinfoには見つからず、リレーションの結合リストにあるはずです。
そのような句を使用するパスは"パラメータ化されたパス"と呼ばれます。
このようなパスでは、選択された結合句(群)で使用されているリレーション(群)をparam_infoの適合する値から特定しなければなりません;その値を計算するにはget_baserel_parampathinfo
を使用します。
GetForeignPlan
では、結合句のlocal_variable部分がfdw_exprsに追加され、実行時には通常の条件句と同じように動作します。
UPDATEやDELETEのプランを生成しているとき、
PlanForeignModify
は、事前にスキャンプラン生成関数で作られたbaserel->fdw_privateデータを使うために、その外部テーブルのためのRelOptInfo構造体を検索することができます。
しかしながら、INSERTでは対象テーブルはスキャンされないので対応するRelOptInfoは存在しません。
PlanForeignModify
から返されるListには、ForeignScanプランノードのfdw_privateリストと同様に、copyObject
がコピーの仕方を知っている構造体しか保持してはいけないという制約があります。
同時更新をサポートする外部データソースに対するUPDATEやDELETEでは、ForeignScan操作はFDWがフェッチする行を、できればSELECT FOR UPDATEに相当するものを用いてロックすることが推奨されています。 また、外部テーブルがSELECT FOR UPDATE/SHAREで参照される場合に、FDWは取り出し時に行をロックすることを選択することもできます。そうでない場合、外部テーブルに関する限りFOR UPDATEやFOR SHAREといったオプションは基本的に何もしません。 この挙動は、習慣的に行ロックは可能な限り遅延されるという、ローカルテーブルに対する操作と全く異なるセマンティクスをもたらすかもしれません。 ローカルで適用される制約や結合条件で除外されるリモート行でさえもロックされるかもしれません。 しかしながら、ローカルセマンティクスと完全に一致させることは、各行について追加のリモートアクセスが必要となるうえ、外部データソースが提供するロックセマンティクスに依存するためおそらく不可能でしょう。