カスタムスキャンは完成した計画ツリー内で、以下の構造体を使って表現されます。
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コールバックが呼び出されることで、カスタムスキャンプロバイダがその他の必要なことを実行する機会が与えられます。