カスタムスキャンは完成した計画ツリー内で、以下の構造体を使って表現されます。
typedef struct CustomScan { Scan scan; uint32 flags; List *custom_plans; List *custom_exprs; List *custom_private; List *custom_scan_tlist; Bitmapset *custom_relids; const CustomScanMethods *methods; } CustomScan;
scan
は他のすべてのスキャンと同じく、推定コスト、対象のリスト、制約などを含めて初期化される必要があります。
flags
はCustomPath
と同じ意味のビットマスクです。
custom_plans
は子のPlan
ノードを格納するために使うことができます。
custom_exprs
はsetrefs.c
およびsubselect.c
によって作成される必要がある式のツリーを格納するために使われます。
一方でcustom_private
はカスタムスキャンプロバイダ自体によってのみ使用されるその他のプライベートデータを格納するために使われます。
custom_scan_tlist
はベースリレーションをスキャンするときはNILとすることができます。
これはカスタムスキャンがベースリレーションの行の型と一致するスキャンタプルを返すことを意味します。
それ以外の場合は、実際のスキャンタプルを表現する対象のリストとなります。
custom_scan_tlist
は結合の場合には提供される必要があります。
また、カスタムスキャンプロバイダがVarでない式を計算できる場合はスキャン用に提供することができます。
custom_relids
は、コアコードにより、このスキャンノードが処理するリレーションの集合(範囲テーブルのインデックス)にセットされます。
ただし、このスキャンが結合を置換する場合は例外で、ただ1つのメンバだけになります。
methods
は必要なカスタムスキャンメソッドを実装しているオブジェクト(通常は静的に割り当てられる)を指していなければなりません。
これについては以下で詳しく説明します。
CustomScan
がリレーションを1つだけスキャンするときは、scan.scanrelid
はスキャンされるテーブルの範囲テーブルのインデックスである必要があります。
結合を置換するときはscan.scanrelid
はゼロになります。
計画ツリーはcopyObject
により複製できる必要があるので、「custom」フィールド内に格納されるすべてのデータは、その関数が処理できるノードから構成されていなければなりません。
また、カスタムスキャンプロバイダはCustomScan
を組み込んでいる大きな構造体をCustomScan
の構造体で代替することができません。
CustomPath
やCustomScanState
に対してはこれが可能です。
Node *(*CreateCustomScanState) (CustomScan *cscan);
このCustomScan
にCustomScanState
の領域を割り当てます。
多くのプロバイダは、より大きな構造体の最初のフィールドとしてこれを組み込もうとするので、実際の割り当ては通常のCustomScanState
が必要とするよりも多くくなることが多いでしょう。
戻り値では、ノードのタグとmethods
が適切に設定されている必要がありますが、その他のフィールドはこの段階ではゼロのままになっています。
ExecInitCustomScan
が基本的な初期化をした後、BeginCustomScan
コールバックが呼び出されることで、カスタムスキャンプロバイダがその他の必要なことを実行する機会が与えられます。