The TSM handler function returns a palloc'd <type>TsmRoutine</type> struct
containing pointers to the support functions described below. Most of
the functions are required, but some are optional, and those pointers can
be NULL.
TSMハンドラ関数は、以下に示すサポート関数へのポインタを含むpallocされたTsmRoutine
構造体を返します。
大半の関数は必須ですが、いくつかオプションのものがあり、そうした関数へのポインタはNULLにできます。
void SampleScanGetSampleSize (PlannerInfo *root, RelOptInfo *baserel, List *paramexprs, BlockNumber *pages, double *tuples);
This function is called during planning. It must estimate the number of
relation pages that will be read during a sample scan, and the number of
tuples that will be selected by the scan. (For example, these might be
determined by estimating the sampling fraction, and then multiplying
the <literal>baserel->pages</literal> and <literal>baserel->tuples</literal>
numbers by that, being sure to round the results to integral values.)
The <literal>paramexprs</literal> list holds the expression(s) that are
parameters to the <literal>TABLESAMPLE</literal> clause. It is recommended to
use <function>estimate_expression_value()</function> to try to reduce these
expressions to constants, if their values are needed for estimation
purposes; but the function must provide size estimates even if they cannot
be reduced, and it should not fail even if the values appear invalid
(remember that they're only estimates of what the run-time values will be).
The <literal>pages</literal> and <literal>tuples</literal> parameters are outputs.
この関数はプランニングの際に呼び出されます。
サンプルスキャン中に読み出すリレーションのページ数と、スキャン中に選択されるタプル行数の見積もりを行わなければなりません。
(たとえば、サンプル比率を推定し、それにbaserel->pages
とbaserel->tuples
を掛け、整数値になるように丸めることで見積もりが可能となるでしょう。)
paramexprs
リストは、TABLESAMPLE
句への引数となる式を格納します。
見積のためにその推測値が必要なら、estimate_expression_value()
を使ってこれらの式を定数に簡約化してみることをおすすめします。
ただし、関数は簡約化ができない場合でもサイズに関する見積は提供しなければなりませんし、値が正しくない場合でも関数がエラーになってはいけません(推測値は、実行時には値がそうなるであろうということに過ぎないことを思い出してください)。
pages
とtuples
パラメータは出力です。
void InitSampleScan (SampleScanState *node, int eflags);
Initialize for execution of a SampleScan plan node.
This is called during executor startup.
It should perform any initialization needed before processing can start.
The <structname>SampleScanState</structname> node has already been created, but
its <structfield>tsm_state</structfield> field is NULL.
The <function>InitSampleScan</function> function can palloc whatever internal
state data is needed by the sampling method, and store a pointer to
it in <literal>node->tsm_state</literal>.
Information about the table to scan is accessible through other fields
of the <structname>SampleScanState</structname> node (but note that the
<literal>node->ss.ss_currentScanDesc</literal> scan descriptor is not set
up yet).
<literal>eflags</literal> contains flag bits describing the executor's
operating mode for this plan node.
SampleScan計画ノードを実行するための初期化を行います。
この関数はエグゼキュータの起動時に呼び出されます。
処理を開始する前に必要な初期化をすべて行う必要があります。
SampleScanState
ノードは作成済みですが、tsm_state
フィールドはNULLです。
InitSampleScan
関数はサンプリングメソッドが必要とする内部データをすべてpallocし、node->tsm_state
に格納します。
スキャン対象のテーブルに関する情報はSampleScanState
ノードの他のフィールドを通じてアクセスできます
(ただし、node->ss.ss_currentScanDesc
スキャンディスクリプタはまだ設定されていません)。
eflags
には、この計画ノードにおけるエグゼキュータの動作モードを記述するフラグビットが含まれます。
When <literal>(eflags & EXEC_FLAG_EXPLAIN_ONLY)</literal> is true,
the scan will not actually be performed, so this function should only do
the minimum required to make the node state valid for <command>EXPLAIN</command>
and <function>EndSampleScan</function>.
(eflags & EXEC_FLAG_EXPLAIN_ONLY)
が真ならば、スキャンは実際には行われず、この関数はEXPLAIN
とEndSampleScan
にとってノードの状態が意味のあるように最低限必要な処理を行うことになります。
This function can be omitted (set the pointer to NULL), in which case
<function>BeginSampleScan</function> must perform all initialization needed
by the sampling method.
この関数は(ポインタをNULLにすることにより)省略できますが、この場合、BeginSampleScan
がサンプリングメソッドに必要なすべての初期化を行なわなければなりません。
void BeginSampleScan (SampleScanState *node, Datum *params, int nparams, uint32 seed);
Begin execution of a sampling scan.
This is called just before the first attempt to fetch a tuple, and
may be called again if the scan needs to be restarted.
Information about the table to scan is accessible through fields
of the <structname>SampleScanState</structname> node (but note that the
<literal>node->ss.ss_currentScanDesc</literal> scan descriptor is not set
up yet).
The <literal>params</literal> array, of length <literal>nparams</literal>, contains the
values of the parameters supplied in the <literal>TABLESAMPLE</literal> clause.
These will have the number and types specified in the sampling
method's <literal>parameterTypes</literal> list, and have been checked
to not be null.
<literal>seed</literal> contains a seed to use for any random numbers generated
within the sampling method; it is either a hash derived from the
<literal>REPEATABLE</literal> value if one was given, or the result
of <literal>random()</literal> if not.
サンプルスキャンの実行を開始します。
これははじめてタプルを取得する直前に呼び出されます。
また、再スキャンを行う必要が出た場合にも呼び出されます。
スキャン対象のテーブルに関する情報はSampleScanState
ノードのフィールドを通じてアクセスできます
(ただし、node->ss.ss_currentScanDesc
スキャンディスクリプタはまだ設定されていません)。
nparams
の長さを持つparams
配列は、TABLESAMPLE
句で指定された引数の値を保持しています。
これらは、サンプリングメソッドのparameterTypes
リストで指定された数と型を持ち、NULLでないことがチェック済みです。
seed
には、サンプリングメソッド内で使われる乱数のために使われるシードが格納されます。
これは、REPEATABLE
の値が指定されている場合はそこから派生したハッシュか、でなければrandom()
の結果です。
This function may adjust the fields <literal>node->use_bulkread</literal>
and <literal>node->use_pagemode</literal>.
If <literal>node->use_bulkread</literal> is <literal>true</literal>, which it is by
default, the scan will use a buffer access strategy that encourages
recycling buffers after use. It might be reasonable to set this
to <literal>false</literal> if the scan will visit only a small fraction of the
table's pages.
If <literal>node->use_pagemode</literal> is <literal>true</literal>, which it is by
default, the scan will perform visibility checking in a single pass for
all tuples on each visited page. It might be reasonable to set this
to <literal>false</literal> if the scan will select only a small fraction of the
tuples on each visited page. That will result in fewer tuple visibility
checks being performed, though each one will be more expensive because it
will require more locking.
この関数はnode->use_bulkread
とnode->use_pagemode
フィールドによって動作を変更します。
node->use_bulkread
がtrue
なら(これはデフォルトです)、スキャンは使用後のバッファの再利用を推奨するバッファアクセス戦略を使います。
テーブルのわずかな部分だけをスキャンがアクセスするようなら、false
にするのが妥当かもしれません。
node->use_pagemode
がtrue
なら(これはデフォルトです)、スキャンはアクセスするページ上のすべてのタプルに対して一括で可視性チェックを行います。
スキャンがアクセスするページ上のわずかな部分のタプルだけを選択するのであれば、false
にするのが妥当かもしれません。
これにより、より少ないタプルに対して可視性チェックが行われます。
ただし、個々の操作はより高くつきます。
というのも、より多くのロックが必要になるからです。
If the sampling method is
marked <literal>repeatable_across_scans</literal>, it must be able to
select the same set of tuples during a rescan as it did originally, that is
a fresh call of <function>BeginSampleScan</function> must lead to selecting the
same tuples as before (if the <literal>TABLESAMPLE</literal> parameters
and seed don't change).
サンプリングメソッドにrepeatable_across_scans
という印があれば、最初にスキャンした時と同じタプルの集合を、再スキャンでも選択できることになります。
つまり、新しいBeginSampleScan
が、前回と同じタプルを選択することになるわけです。
(もしTABLESAMPLE
の引数とシードが変わらなければ、の話ですが)
BlockNumber NextSampleBlock (SampleScanState *node, BlockNumber nblocks);
Returns the block number of the next page to be scanned, or
<literal>InvalidBlockNumber</literal> if no pages remain to be scanned.
次にスキャンするページのブロック番号を返します。
もはやスキャンするページがない場合にはInvalidBlockNumber
を返します。
This function can be omitted (set the pointer to NULL), in which case the core code will perform a sequential scan of the entire relation. Such a scan can use synchronized scanning, so that the sampling method cannot assume that the relation pages are visited in the same order on each scan. この関数は(ポインタをNULLにすることにより)省略できます。 この場合コアのコードはリレーション全体をシーケンシャルスキャンします。 そのようなスキャンは同期スキャンを行う可能性があるので、毎回のスキャンで同じ順番でリレーションのページをアクセスするとは、サンプリングメソッドは仮定できません。
OffsetNumber NextSampleTuple (SampleScanState *node, BlockNumber blockno, OffsetNumber maxoffset);
Returns the offset number of the next tuple to be sampled on the
specified page, or <literal>InvalidOffsetNumber</literal> if no tuples remain to
be sampled. <literal>maxoffset</literal> is the largest offset number in use
on the page.
サンプル対象の指定ページ内の次のタプルのオフセット番号を返します。
サンプル対象のタプルが残っていない場合は、InvalidOffsetNumber
を返します。
maxoffset
は、使用中のページ中の最大オフセット番号です。
<function>NextSampleTuple</function> is not explicitly told which of the offset
numbers in the range <literal>1 .. maxoffset</literal> actually contain valid
tuples. This is not normally a problem since the core code ignores
requests to sample missing or invisible tuples; that should not result in
any bias in the sample. However, if necessary, the function can use
<literal>node->donetuples</literal> to examine how many of the tuples
it returned were valid and visible.
NextSampleTuple
は、範囲1 .. maxoffset
の中のどのオフセット番号が有効なタプルにあたるのかは明示的には教えてくれません。
コアのコードは、存在しない、あるいは不可視のタプルを対象とするサンプルの要求は無視するため、通常これは問題にはなりません。サンプルの偏りも起きません。
それでも必要ならば、関数はnode->donetuples
を使って、返されたタプルのうちのいくつが有効で可視であったのかを調べることができます。
<function>NextSampleTuple</function> must <emphasis>not</emphasis> assume
that <literal>blockno</literal> is the same page number returned by the most
recent <function>NextSampleBlock</function> call. It was returned by some
previous <function>NextSampleBlock</function> call, but the core code is allowed
to call <function>NextSampleBlock</function> in advance of actually scanning
pages, so as to support prefetching. It is OK to assume that once
sampling of a given page begins, successive <function>NextSampleTuple</function>
calls all refer to the same page until <literal>InvalidOffsetNumber</literal> is
returned.
NextSampleTuple
は、直近のNextSampleBlock
の呼び出しが返したページ番号とblockno
が同じであると見なすべきではありません。
ページ番号は、以前のNextSampleBlock
の呼び出しが返したものではありますが、コアのコードは、先読みのために実際のスキャンに先立ってNextSampleBlock
を呼び出すことが認められています。
一旦あるページのサンプリングが開始すれば、InvalidOffsetNumber
が返るまでは、続くNextSampleTuple
に呼び出しがすべて同じページを参照すると見なすことは問題ありません。
void EndSampleScan (SampleScanState *node);
End the scan and release resources. It is normally not important to release palloc'd memory, but any externally-visible resources should be cleaned up. This function can be omitted (set the pointer to NULL) in the common case where no such resources exist. スキャンを終了し、リソースを解放します。 通常pallocされたメモリを解放するのは重要なことではありませんが、外部から見えるリソースはすべて解放しなければなりません。 そのようなリソースが存在しない場合は、この関数は(ポインタをNULLにすることにより)省略できます。