Re: TABLESAMPLE patch is really in pretty sad shape

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: TABLESAMPLE patch is really in pretty sad shape
Дата
Msg-id 20611.1437691149@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: TABLESAMPLE patch is really in pretty sad shape  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: TABLESAMPLE patch is really in pretty sad shape  (Petr Jelinek <petr@2ndquadrant.com>)
Re: TABLESAMPLE patch is really in pretty sad shape  (Peter Eisentraut <peter_e@gmx.net>)
Список pgsql-hackers
I wrote:
> OK, so "InitSampleScan" for a function called at ExecInitSampleScan time
> (which we might as well make optional), and then we'll use BeginSampleScan
> for the function that gets the parameters.  The restart/ReScan function
> goes away since BeginSampleScan will take its place.

Here's a WIP patch implementing things this way.  I've also taken the time
to do a complete code review, and fixed quite a large number of things,
some cosmetic and some not so much.  I have not yet touched the tsm
contrib modules (so they won't even compile...), but I'm reasonably happy
with the state of the core code now.

Barring objections, I plan to fix up the contrib modules to match and
back-patch this into 9.5.

            regards, tom lane

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 0eb991c..59b8a2e 100644
*** a/contrib/pg_stat_statements/pg_stat_statements.c
--- b/contrib/pg_stat_statements/pg_stat_statements.c
*************** JumbleRangeTable(pgssJumbleState *jstate
*** 2297,2302 ****
--- 2297,2303 ----
          {
              case RTE_RELATION:
                  APP_JUMB(rte->relid);
+                 JumbleExpr(jstate, (Node *) rte->tablesample);
                  break;
              case RTE_SUBQUERY:
                  JumbleQuery(jstate, rte->subquery);
*************** JumbleExpr(pgssJumbleState *jstate, Node
*** 2767,2772 ****
--- 2768,2782 ----
                  JumbleExpr(jstate, rtfunc->funcexpr);
              }
              break;
+         case T_TableSampleClause:
+             {
+                 TableSampleClause *tsc = (TableSampleClause *) node;
+
+                 APP_JUMB(tsc->tsmhandler);
+                 JumbleExpr(jstate, (Node *) tsc->args);
+                 JumbleExpr(jstate, (Node *) tsc->repeatable);
+             }
+             break;
          default:
              /* Only a warning, since we can stumble along anyway */
              elog(WARNING, "unrecognized node type: %d",
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 2c2190f..9096ee5 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 279,289 ****
       </row>

       <row>
-       <entry><link
linkend="catalog-pg-tablesample-method"><structname>pg_tablesample_method</structname></link></entry>
-       <entry>table sampling methods</entry>
-      </row>
-
-      <row>
        <entry><link linkend="catalog-pg-tablespace"><structname>pg_tablespace</structname></link></entry>
        <entry>tablespaces within this database cluster</entry>
       </row>
--- 279,284 ----
***************
*** 6132,6252 ****
   </sect1>


-  <sect1 id="catalog-pg-tablesample-method">
-   <title><structname>pg_tabesample_method</structname></title>
-
-   <indexterm zone="catalog-pg-tablesample-method">
-    <primary>pg_am</primary>
-   </indexterm>
-
-   <para>
-    The catalog <structname>pg_tablesample_method</structname> stores
-    information about table sampling methods which can be used in
-    <command>TABLESAMPLE</command> clause of a <command>SELECT</command>
-    statement.
-   </para>
-
-   <table>
-    <title><structname>pg_tablesample_method</> Columns</title>
-
-    <tgroup cols="4">
-     <thead>
-      <row>
-       <entry>Name</entry>
-       <entry>Type</entry>
-       <entry>References</entry>
-       <entry>Description</entry>
-      </row>
-     </thead>
-     <tbody>
-
-      <row>
-       <entry><structfield>oid</structfield></entry>
-       <entry><type>oid</type></entry>
-       <entry></entry>
-       <entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmname</structfield></entry>
-       <entry><type>name</type></entry>
-       <entry></entry>
-       <entry>Name of the sampling method</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmseqscan</structfield></entry>
-       <entry><type>bool</type></entry>
-       <entry></entry>
-       <entry>If true, the sampling method scans the whole table sequentially.
-       </entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmpagemode</structfield></entry>
-       <entry><type>bool</type></entry>
-       <entry></entry>
-       <entry>If true, the sampling method always reads the pages completely.
-       </entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsminit</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry><quote>Initialize the sampling scan</quote> function</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmnextblock</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry><quote>Get next block number</quote> function</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmnexttuple</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry><quote>Get next tuple offset</quote> function</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmexaminetuple</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry>Function which examines the tuple contents and decides if to
-         return it, or zero if none</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmend</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry><quote>End the sampling scan</quote> function</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmreset</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry><quote>Restart the state of sampling scan</quote> function</entry>
-      </row>
-
-      <row>
-       <entry><structfield>tsmcost</structfield></entry>
-       <entry><type>regproc</type></entry>
-       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
-       <entry>Costing function</entry>
-      </row>
-
-     </tbody>
-    </tgroup>
-   </table>
-
-  </sect1>
-
-
   <sect1 id="catalog-pg-tablespace">
    <title><structname>pg_tablespace</structname></title>

--- 6127,6132 ----
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 8e13555..d24249e 100644
*** a/doc/src/sgml/datatype.sgml
--- b/doc/src/sgml/datatype.sgml
*************** SELECT * FROM pg_attribute
*** 4623,4628 ****
--- 4623,4632 ----
     </indexterm>

     <indexterm zone="datatype-pseudo">
+     <primary>tsm_handler</primary>
+    </indexterm>
+
+    <indexterm zone="datatype-pseudo">
      <primary>cstring</primary>
     </indexterm>

*************** SELECT * FROM pg_attribute
*** 4717,4722 ****
--- 4721,4731 ----
         </row>

         <row>
+         <entry><type>tsm_handler</></entry>
+         <entry>A tablesample method handler is declared to return <type>tsm_handler</>.</entry>
+        </row>
+
+        <row>
          <entry><type>record</></entry>
          <entry>Identifies a function returning an unspecified row type.</entry>
         </row>
diff --git a/doc/src/sgml/tablesample-method.sgml b/doc/src/sgml/tablesample-method.sgml
index 48eb7fe..2e2fdcc 100644
*** a/doc/src/sgml/tablesample-method.sgml
--- b/doc/src/sgml/tablesample-method.sgml
***************
*** 1,139 ****
  <!-- doc/src/sgml/tablesample-method.sgml -->

  <chapter id="tablesample-method">
!  <title>Writing A TABLESAMPLE Sampling Method</title>

   <indexterm zone="tablesample-method">
    <primary>tablesample method</primary>
   </indexterm>

   <para>
!   The <command>TABLESAMPLE</command> clause implementation in
!   <productname>PostgreSQL</> supports creating a custom sampling methods.
!   These methods control what sample of the table will be returned when the
!   <command>TABLESAMPLE</command> clause is used.
   </para>

!  <sect1 id="tablesample-method-functions">
!   <title>Tablesample Method Functions</title>

    <para>
!    The tablesample method must provide following set of functions:
    </para>

    <para>
  <programlisting>
  void
! tsm_init (TableSampleDesc *desc,
!          uint32 seed, ...);
  </programlisting>
!    Initialize the tablesample scan. The function is called at the beginning
!    of each relation scan.
    </para>
    <para>
-    Note that the first two parameters are required but you can specify
-    additional parameters which then will be used by the <command>TABLESAMPLE</>
-    clause to determine the required user input in the query itself.
-    This means that if your function will specify additional float4 parameter
-    named percent, the user will have to call the tablesample method with
-    expression which evaluates (or can be coerced) to float4.
-    For example this definition:
  <programlisting>
! tsm_init (TableSampleDesc *desc,
!           uint32 seed, float4 pct);
! </programlisting>
! Will lead to SQL call like this:
! <programlisting>
! ... TABLESAMPLE yourmethod(0.5) ...
  </programlisting>
    </para>

    <para>
! <programlisting>
! BlockNumber
! tsm_nextblock (TableSampleDesc *desc);
! </programlisting>
!    Returns the block number of next page to be scanned. InvalidBlockNumber
!    should be returned if the sampling has reached end of the relation.
    </para>

    <para>
! <programlisting>
! OffsetNumber
! tsm_nexttuple (TableSampleDesc *desc, BlockNumber blockno,
!                OffsetNumber maxoffset);
! </programlisting>
!    Return next tuple offset for the current page. InvalidOffsetNumber should
!    be returned if the sampling has reached end of the page.
    </para>

    <para>
  <programlisting>
  void
! tsm_end (TableSampleDesc *desc);
  </programlisting>
!    The scan has finished, cleanup any left over state.
    </para>

    <para>
! <programlisting>
! void
! tsm_reset (TableSampleDesc *desc);
! </programlisting>
!    The scan needs to rescan the relation again, reset any tablesample method
!    state.
    </para>

    <para>
  <programlisting>
! void
! tsm_cost (PlannerInfo *root, Path *path, RelOptInfo *baserel,
!           List *args, BlockNumber *pages, double *tuples);
  </programlisting>
!    This function is used by optimizer to decide best plan and is also used
!    for output of <command>EXPLAIN</>.
    </para>

    <para>
!    There is one more function which tablesampling method can implement in order
!    to gain more fine grained control over sampling. This function is optional:
    </para>

    <para>
  <programlisting>
! bool
! tsm_examinetuple (TableSampleDesc *desc, BlockNumber blockno,
!                   HeapTuple tuple, bool visible);
  </programlisting>
!    Function that enables the sampling method to examine contents of the tuple
!    (for example to collect some internal statistics). The return value of this
!    function is used to determine if the tuple should be returned to client.
!    Note that this function will receive even invisible tuples but it is not
!    allowed to return true for such tuple (if it does,
!    <productname>PostgreSQL</> will raise an error).
    </para>

    <para>
-   As you can see most of the tablesample method interfaces get the
-   <structname>TableSampleDesc</> as a first parameter. This structure holds
-   state of the current scan and also provides storage for the tablesample
-   method's state. It is defined as following:
  <programlisting>
! typedef struct TableSampleDesc {
!     HeapScanDesc    heapScan;
!     TupleDesc       tupDesc;
!
!     void           *tsmdata;
! } TableSampleDesc;
  </programlisting>
!   Where <structfield>heapScan</> is the descriptor of the physical table scan.
!   It's possible to get table size info from it. The <structfield>tupDesc</>
!   represents the tuple descriptor of the tuples returned by the scan and passed
!   to the <function>tsm_examinetuple()</> interface. The <structfield>tsmdata</>
!   can be used by tablesample method itself to store any state info it might
!   need during the scan. If used by the method, it should be <function>pfree</>d
!   in <function>tsm_end()</> function.
    </para>
   </sect1>

  </chapter>
--- 1,289 ----
  <!-- doc/src/sgml/tablesample-method.sgml -->

  <chapter id="tablesample-method">
!  <title>Writing A Tablesample Sampling Method</title>

   <indexterm zone="tablesample-method">
    <primary>tablesample method</primary>
   </indexterm>

   <para>
!   <productname>PostgreSQL</>'s implementation of the <literal>TABLESAMPLE</>
!   clause supports custom sampling methods, in addition to
!   the <literal>BERNOULLI</> and <literal>SYSTEM</> methods that are required
!   by the SQL standard.  The sampling method determines which rows of the
!   table will be selected when the <literal>TABLESAMPLE</> clause is used.
   </para>

!  <para>
!   At the SQL level, a tablesample method is represented by a single SQL
!   function, typically implemented in C, having the signature
! <programlisting>
! method_name(internal) RETURNS tsm_handler
! </programlisting>
!   The name of the function is the same method name appearing in the
!   <literal>TABLESAMPLE</> clause.  The <type>internal</> argument is a dummy
!   (always having value zero) that simply serves to prevent this function from
!   being called directly from a SQL command.
!   The result of the function must be a palloc'd struct of
!   type <type>TsmRoutine</>, which contains pointers to support functions for
!   the tablesample method.  These support functions are plain C functions and
!   are not visible or callable at the SQL level.  The support functions are
!   described in <xref linkend="tablesample-support-functions">.
!  </para>
!
!  <para>
!   In addition to function pointers, the <type>TsmRoutine</> struct must
!   include these additional fields:
!  </para>
!
!  <variablelist>
!   <varlistentry>
!    <term><literal>List *parameterTypes</literal></term>
!    <listitem>
!     <para>
!      This is an OID list containing the data type OIDs of the parameter(s)
!      that will be accepted by the <literal>TABLESAMPLE</> clause when this
!      sampling method is used.  For example, for the built-in methods, this
!      list contains a single item with value <literal>FLOAT4OID</>, which
!      represents the sampling percentage.  Custom sampling methods can have
!      more or different parameters.
!     </para>
!    </listitem>
!   </varlistentry>
!
!   <varlistentry>
!    <term><literal>bool repeatable_across_queries</literal></term>
!    <listitem>
!     <para>
!      If <literal>true</>, the sampling method can deliver identical samples
!      across successive queries, if the same parameters
!      and <literal>REPEATABLE</> seed value are supplied each time and the
!      table contents have not changed.  When this is <literal>false</>,
!      the <literal>REPEATABLE</> clause is not accepted for use with the
!      sampling method.
!     </para>
!    </listitem>
!   </varlistentry>
!
!   <varlistentry>
!    <term><literal>bool repeatable_across_scans</literal></term>
!    <listitem>
!     <para>
!      If <literal>true</>, the sampling method can deliver identical samples
!      across successive scans in the same query (assuming unchanging
!      parameters, seed value, and snapshot).
!      When this is <literal>false</>, the planner will not select plans that
!      would require scanning the sampled table more than once, since that
!      might result in inconsistent query output.
!     </para>
!    </listitem>
!   </varlistentry>
!  </variablelist>
!
!  <para>
!   The <type>TsmRoutine</> struct type is declared
!   in <filename>src/include/access/tsmapi.h</>, which see for additional
!   details.
!  </para>
!
!  <sect1 id="tablesample-support-functions">
!   <title>Tablesample Support Functions</title>

    <para>
!    The TSM handler function returns a palloc'd <type>TsmRoutine</> 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.
    </para>

    <para>
  <programlisting>
  void
! SampleScanCost (PlannerInfo *root,
!                 RelOptInfo *baserel,
!                 List *paramexprs,
!                 BlockNumber *pages,
!                 double *tuples);
  </programlisting>
!
!    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</> and <literal>baserel->tuples</>
!    numbers by that, being sure to round the results to integral values.)
!    The <literal>paramexprs</> list holds the expression(s) that are
!    parameters to the <literal>TABLESAMPLE</> clause.  It is recommended to
!    use <function>estimate_expression_value()</> to try to reduce these
!    expressions to constants, if their values are needed for estimation
!    purposes; but the function must provide cost 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</> and <literal>tuples</> parameters are outputs.
    </para>
+
    <para>
  <programlisting>
! void
! InitSampleScan (SampleScanState *node,
!                 int eflags);
  </programlisting>
+
+    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</> node has already been created, but
+    its <structfield>tsm_state</> field is NULL.
+    The <function>InitSampleScan</> function can palloc whatever internal
+    state data is needed by the tablesample method, and store a pointer to
+    it in <literal>node->tsm_state</>.
+    Information about the table to scan is accessible through other fields
+    of the <structname>SampleScanState</> node (but note that the
+    <literal>node->ss.ss_currentScanDesc</> scan descriptor is not set
+    up yet).
+    <literal>eflags</> contains flag bits describing the executor's
+    operating mode for this plan node.
    </para>

    <para>
!    When <literal>(eflags & EXEC_FLAG_EXPLAIN_ONLY)</> 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</>
!    and <function>EndSampleScan</>.
    </para>

    <para>
!    This function can be omitted (set the pointer to NULL), in which case
!    <function>BeginSampleScan</> must perform all initialization needed
!    by the tablesample method.
    </para>

    <para>
  <programlisting>
  void
! BeginSampleScan (SampleScanState *node,
!                  Datum *params,
!                  int nparams,
!                  uint32 seed);
  </programlisting>
!
!    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</> node (but note that the
!    <literal>node->ss.ss_currentScanDesc</> scan descriptor is not set
!    up yet).
!    The <literal>params</> array, of length <literal>nparams</>, contains the
!    values of the parameters supplied in the <literal>TABLESAMPLE</> clause.
!    These will have the number and types specified in the tablesample
!    method's <literal>parameterTypes</literal> list, and have been checked
!    to not be null.
!    <literal>seed</> contains a seed to use for any random numbers generated
!    within the sampling method; it is either a hash derived from the
!    <literal>REPEATABLE</> value if one was given, or the result
!    of <literal>random()</> if not.
    </para>

    <para>
!    This function may adjust the fields <literal>node->use_bulkread</>
!    and <literal>node->use_pagemode</>.
!    If <literal>node->use_bulkread</> is <literal>true</>, 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</> if the scan will visit only a small fraction of the
!    table's pages.
!    If <literal>node->use_pagemode</> is <literal>true</>, 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</> 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.
!   </para>
!
!   <para>
!    If the tablesample 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</> must lead to selecting the
!    same tuples as before (if the <literal>TABLESAMPLE</> parameters
!    and seed don't change).
    </para>

    <para>
  <programlisting>
! BlockNumber
! NextSampleBlock (SampleScanState *node);
  </programlisting>
!
!    Returns the block number of the next page to be scanned, or
!    <literal>InvalidBlockNumber</> if no pages remain to be scanned.
    </para>

    <para>
!    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 tablesample method
!    cannot assume that the relation pages are visited in the same order on
!    each scan.
    </para>

    <para>
  <programlisting>
! OffsetNumber
! NextSampleTuple (SampleScanState *node,
!                  BlockNumber blockno,
!                  OffsetNumber maxoffset);
  </programlisting>
!
!    Returns the offset number of the next tuple to be sampled on the
!    specified page, or <literal>InvalidOffsetNumber</> if no tuples remain to
!    be sampled.  <literal>maxoffset</> is the largest offset number in use
!    on the page.
    </para>

+   <note>
+    <para>
+     <function>NextSampleTuple</> is not explicitly told which of the offset
+     numbers in the range <literal>1 .. maxoffset</> 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
+     examine <literal>node->ss.ss_currentScanDesc->rs_vistuples[]</>
+     to identify which tuples are valid and visible.  (This
+     requires <literal>node->use_pagemode</> to be <literal>true</>.)
+    </para>
+   </note>
+
+   <note>
+    <para>
+     <function>NextSampleTuple</> must <emphasis>not</> assume
+     that <literal>blockno</> is the same page number returned by the most
+     recent <function>NextSampleBlock</> call.  It was returned by some
+     previous <function>NextSampleBlock</> call, but the core code is allowed
+     to call <function>NextSampleBlock</> 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</>
+     calls all refer to the same page until <literal>InvalidOffsetNumber</> is
+     returned.
+    </para>
+   </note>
+
    <para>
  <programlisting>
! void
! EndSampleScan (SampleScanState *node);
  </programlisting>
!
!    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.
    </para>
+
   </sect1>

  </chapter>
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6f4ff27..050efdc 100644
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
*************** bool        synchronize_seqscans = true;
*** 80,87 ****
  static HeapScanDesc heap_beginscan_internal(Relation relation,
                          Snapshot snapshot,
                          int nkeys, ScanKey key,
!                       bool allow_strat, bool allow_sync, bool allow_pagemode,
!                         bool is_bitmapscan, bool is_samplescan,
                          bool temp_snap);
  static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
                      TransactionId xid, CommandId cid, int options);
--- 80,90 ----
  static HeapScanDesc heap_beginscan_internal(Relation relation,
                          Snapshot snapshot,
                          int nkeys, ScanKey key,
!                         bool allow_strat,
!                         bool allow_sync,
!                         bool allow_pagemode,
!                         bool is_bitmapscan,
!                         bool is_samplescan,
                          bool temp_snap);
  static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
                      TransactionId xid, CommandId cid, int options);
*************** static const int MultiXactStatusLock[Max
*** 207,213 ****
   * ----------------
   */
  static void
! initscan(HeapScanDesc scan, ScanKey key, bool is_rescan)
  {
      bool        allow_strat;
      bool        allow_sync;
--- 210,216 ----
   * ----------------
   */
  static void
! initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
  {
      bool        allow_strat;
      bool        allow_sync;
*************** initscan(HeapScanDesc scan, ScanKey key,
*** 257,268 ****
          scan->rs_strategy = NULL;
      }

!     if (is_rescan)
      {
          /*
!          * If rescan, keep the previous startblock setting so that rewinding a
!          * cursor doesn't generate surprising results.  Reset the syncscan
!          * setting, though.
           */
          scan->rs_syncscan = (allow_sync && synchronize_seqscans);
      }
--- 260,271 ----
          scan->rs_strategy = NULL;
      }

!     if (keep_startblock)
      {
          /*
!          * When rescanning, we want to keep the previous startblock setting,
!          * so that rewinding a cursor doesn't generate surprising results.
!          * Reset the active syncscan setting, though.
           */
          scan->rs_syncscan = (allow_sync && synchronize_seqscans);
      }
*************** heap_openrv_extended(const RangeVar *rel
*** 1313,1318 ****
--- 1316,1325 ----
  /* ----------------
   *        heap_beginscan    - begin relation scan
   *
+  * heap_beginscan is the "standard" case.
+  *
+  * heap_beginscan_catalog differs in setting up its own temporary snapshot.
+  *
   * heap_beginscan_strat offers an extended API that lets the caller control
   * whether a nondefault buffer access strategy can be used, and whether
   * syncscan can be chosen (possibly resulting in the scan not starting from
*************** heap_openrv_extended(const RangeVar *rel
*** 1323,1330 ****
   * really quite unlike a standard seqscan, there is just enough commonality
   * to make it worth using the same data structure.
   *
!  * heap_beginscan_samplingscan is alternate entry point for setting up a
!  * HeapScanDesc for a TABLESAMPLE scan.
   * ----------------
   */
  HeapScanDesc
--- 1330,1340 ----
   * really quite unlike a standard seqscan, there is just enough commonality
   * to make it worth using the same data structure.
   *
!  * heap_beginscan_sampling is an alternative entry point for setting up a
!  * HeapScanDesc for a TABLESAMPLE scan.  As with bitmap scans, it's worth
!  * using the same data structure although the behavior is rather different.
!  * In addition to the options offered by heap_beginscan_strat, this call
!  * also allows control of whether page-mode visibility checking is used.
   * ----------------
   */
  HeapScanDesc
*************** heap_beginscan_bm(Relation relation, Sna
*** 1366,1383 ****
  HeapScanDesc
  heap_beginscan_sampling(Relation relation, Snapshot snapshot,
                          int nkeys, ScanKey key,
!                         bool allow_strat, bool allow_pagemode)
  {
      return heap_beginscan_internal(relation, snapshot, nkeys, key,
!                                    allow_strat, false, allow_pagemode,
                                     false, true, false);
  }

  static HeapScanDesc
  heap_beginscan_internal(Relation relation, Snapshot snapshot,
                          int nkeys, ScanKey key,
!                       bool allow_strat, bool allow_sync, bool allow_pagemode,
!                       bool is_bitmapscan, bool is_samplescan, bool temp_snap)
  {
      HeapScanDesc scan;

--- 1376,1397 ----
  HeapScanDesc
  heap_beginscan_sampling(Relation relation, Snapshot snapshot,
                          int nkeys, ScanKey key,
!                       bool allow_strat, bool allow_sync, bool allow_pagemode)
  {
      return heap_beginscan_internal(relation, snapshot, nkeys, key,
!                                    allow_strat, allow_sync, allow_pagemode,
                                     false, true, false);
  }

  static HeapScanDesc
  heap_beginscan_internal(Relation relation, Snapshot snapshot,
                          int nkeys, ScanKey key,
!                         bool allow_strat,
!                         bool allow_sync,
!                         bool allow_pagemode,
!                         bool is_bitmapscan,
!                         bool is_samplescan,
!                         bool temp_snap)
  {
      HeapScanDesc scan;

*************** heap_rescan(HeapScanDesc scan,
*** 1462,1467 ****
--- 1476,1502 ----
  }

  /* ----------------
+  *        heap_rescan_set_params    - restart a relation scan after changing params
+  *
+  * This call allows changing the buffer strategy, syncscan, and pagemode
+  * options before starting a fresh scan.  Note that although the actual use
+  * of syncscan might change (effectively, enabling or disabling reporting),
+  * the previously selected startblock will be kept.
+  * ----------------
+  */
+ void
+ heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
+                        bool allow_strat, bool allow_sync, bool allow_pagemode)
+ {
+     /* adjust parameters */
+     scan->rs_allow_strat = allow_strat;
+     scan->rs_allow_sync = allow_sync;
+     scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
+     /* ... and rescan */
+     heap_rescan(scan, key);
+ }
+
+ /* ----------------
   *        heap_endscan    - end relation scan
   *
   *        See how to integrate with index scans.
diff --git a/src/backend/access/tablesample/Makefile b/src/backend/access/tablesample/Makefile
index 46eeb59..68d9ab2 100644
*** a/src/backend/access/tablesample/Makefile
--- b/src/backend/access/tablesample/Makefile
***************
*** 1,10 ****
  #-------------------------------------------------------------------------
  #
  # Makefile--
! #    Makefile for utils/tablesample
  #
  # IDENTIFICATION
! #    src/backend/utils/tablesample/Makefile
  #
  #-------------------------------------------------------------------------

--- 1,10 ----
  #-------------------------------------------------------------------------
  #
  # Makefile--
! #    Makefile for access/tablesample
  #
  # IDENTIFICATION
! #    src/backend/access/tablesample/Makefile
  #
  #-------------------------------------------------------------------------

*************** subdir = src/backend/access/tablesample
*** 12,17 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global

! OBJS = tablesample.o system.o bernoulli.o

  include $(top_srcdir)/src/backend/common.mk
--- 12,17 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global

! OBJS = bernoulli.o system.o tablesample.o

  include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/tablesample/bernoulli.c b/src/backend/access/tablesample/bernoulli.c
index 0a53900..97d6407 100644
*** a/src/backend/access/tablesample/bernoulli.c
--- b/src/backend/access/tablesample/bernoulli.c
***************
*** 1,233 ****
  /*-------------------------------------------------------------------------
   *
   * bernoulli.c
!  *      interface routines for BERNOULLI tablesample method
   *
!  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
   *
   * IDENTIFICATION
!  *      src/backend/utils/tablesample/bernoulli.c
   *
   *-------------------------------------------------------------------------
   */

  #include "postgres.h"

! #include "fmgr.h"

! #include "access/tablesample.h"
! #include "access/relscan.h"
! #include "nodes/execnodes.h"
! #include "nodes/relation.h"
  #include "optimizer/clauses.h"
! #include "storage/bufmgr.h"
! #include "utils/sampling.h"


! /* tsdesc */
  typedef struct
  {
      uint32        seed;            /* random seed */
-     BlockNumber startblock;        /* starting block, we use ths for syncscan
-                                  * support */
-     BlockNumber nblocks;        /* number of blocks */
-     BlockNumber blockno;        /* current block */
-     float4        probability;    /* probabilty that tuple will be returned
-                                  * (0.0-1.0) */
      OffsetNumber lt;            /* last tuple returned from current block */
-     SamplerRandomState randstate;        /* random generator tsdesc */
  } BernoulliSamplerData;

  /*
!  * Initialize the state.
   */
  Datum
! tsm_bernoulli_init(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     uint32        seed = PG_GETARG_UINT32(1);
!     float4        percent = PG_ARGISNULL(2) ? -1 : PG_GETARG_FLOAT4(2);
!     HeapScanDesc scan = tsdesc->heapScan;
!     BernoulliSamplerData *sampler;
!
!     if (percent < 0 || percent > 100)
!         ereport(ERROR,
!                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
!                  errmsg("invalid sample size"),
!                  errhint("Sample size must be numeric value between 0 and 100 (inclusive).")));
!
!     sampler = palloc0(sizeof(BernoulliSamplerData));
!
!     /* Remember initial values for reinit */
!     sampler->seed = seed;
!     sampler->startblock = scan->rs_startblock;
!     sampler->nblocks = scan->rs_nblocks;
!     sampler->blockno = InvalidBlockNumber;
!     sampler->probability = percent / 100;
!     sampler->lt = InvalidOffsetNumber;
!     sampler_random_init_state(sampler->seed, sampler->randstate);

!     tsdesc->tsmdata = (void *) sampler;

!     PG_RETURN_VOID();
  }

  /*
!  * Get next block number to read or InvalidBlockNumber if we are at the
!  * end of the relation.
   */
! Datum
! tsm_bernoulli_nextblock(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     BernoulliSamplerData *sampler = (BernoulliSamplerData *) tsdesc->tsmdata;

!     /*
!      * Bernoulli sampling scans all blocks on the table and supports syncscan
!      * so loop from startblock to startblock instead of from 0 to nblocks.
!      */
!     if (sampler->blockno == InvalidBlockNumber)
!         sampler->blockno = sampler->startblock;
      else
      {
!         sampler->blockno++;

!         if (sampler->blockno >= sampler->nblocks)
!             sampler->blockno = 0;

!         if (sampler->blockno == sampler->startblock)
!             PG_RETURN_UINT32(InvalidBlockNumber);
!     }

!     PG_RETURN_UINT32(sampler->blockno);
  }

  /*
!  * Get next tuple from current block.
!  *
!  * This method implements the main logic in bernoulli sampling.
!  * The algorithm simply generates new random number (in 0.0-1.0 range) and if
!  * it falls within user specified probability (in the same range) return the
!  * tuple offset.
!  *
!  * It is ok here to return tuple offset without knowing if tuple is visible
!  * and not check it via examinetuple. The reason for that is that we do the
!  * coinflip (random number generation) for every tuple in the table. Since all
!  * tuples have same probability of being returned the visible and invisible
!  * tuples will be returned in same ratio as they have in the actual table.
!  * This means that there is no skew towards either visible or invisible tuples
!  * and the number of visible tuples returned from the executor node should
!  * match the fraction of visible tuples which was specified by user.
   *
!  * This is faster than doing the coinflip in examinetuple because we don't
!  * have to do visibility checks on uninteresting tuples.
   *
!  * If we reach end of the block return InvalidOffsetNumber which tells
   * SampleScan to go to next block.
   */
! Datum
! tsm_bernoulli_nexttuple(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     OffsetNumber maxoffset = PG_GETARG_UINT16(2);
!     BernoulliSamplerData *sampler = (BernoulliSamplerData *) tsdesc->tsmdata;
      OffsetNumber tupoffset = sampler->lt;
!     float4        probability = sampler->probability;

      if (tupoffset == InvalidOffsetNumber)
          tupoffset = FirstOffsetNumber;
      else
          tupoffset++;

      /*
!      * Loop over tuple offsets until the random generator returns value that
!      * is within the probability of returning the tuple or until we reach end
!      * of the block.
       *
!      * (This is our implementation of bernoulli trial)
       */
!     while (sampler_random_fract(sampler->randstate) > probability)
      {
!         tupoffset++;

!         if (tupoffset > maxoffset)
              break;
      }

      if (tupoffset > maxoffset)
-         /* Tell SampleScan that we want next block. */
          tupoffset = InvalidOffsetNumber;

      sampler->lt = tupoffset;

!     PG_RETURN_UINT16(tupoffset);
! }
!
! /*
!  * Cleanup method.
!  */
! Datum
! tsm_bernoulli_end(PG_FUNCTION_ARGS)
! {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!
!     pfree(tsdesc->tsmdata);
!
!     PG_RETURN_VOID();
! }
!
! /*
!  * Reset tsdesc (called by ReScan).
!  */
! Datum
! tsm_bernoulli_reset(PG_FUNCTION_ARGS)
! {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     BernoulliSamplerData *sampler = (BernoulliSamplerData *) tsdesc->tsmdata;
!
!     sampler->blockno = InvalidBlockNumber;
!     sampler->lt = InvalidOffsetNumber;
!     sampler_random_init_state(sampler->seed, sampler->randstate);
!
!     PG_RETURN_VOID();
! }
!
! /*
!  * Costing function.
!  */
! Datum
! tsm_bernoulli_cost(PG_FUNCTION_ARGS)
! {
!     PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
!     Path       *path = (Path *) PG_GETARG_POINTER(1);
!     RelOptInfo *baserel = (RelOptInfo *) PG_GETARG_POINTER(2);
!     List       *args = (List *) PG_GETARG_POINTER(3);
!     BlockNumber *pages = (BlockNumber *) PG_GETARG_POINTER(4);
!     double       *tuples = (double *) PG_GETARG_POINTER(5);
!     Node       *pctnode;
!     float4        samplesize;
!
!     *pages = baserel->pages;
!
!     pctnode = linitial(args);
!     pctnode = estimate_expression_value(root, pctnode);
!
!     if (IsA(pctnode, RelabelType))
!         pctnode = (Node *) ((RelabelType *) pctnode)->arg;
!
!     if (IsA(pctnode, Const))
!     {
!         samplesize = DatumGetFloat4(((Const *) pctnode)->constvalue);
!         samplesize /= 100.0;
!     }
!     else
!     {
!         /* Default samplesize if the estimation didn't return Const. */
!         samplesize = 0.1f;
!     }
!
!     *tuples = path->rows * samplesize;
!     path->rows = *tuples;
!
!     PG_RETURN_VOID();
  }
--- 1,224 ----
  /*-------------------------------------------------------------------------
   *
   * bernoulli.c
!  *      support routines for BERNOULLI tablesample method
   *
!  * To ensure repeatability of samples, it is necessary that selection of a
!  * given tuple be history-independent; otherwise syncscanning would break
!  * repeatability, to say nothing of logically-irrelevant maintenance such
!  * as physical extension or shortening of the relation.
!  *
!  * To achieve that, we proceed by hashing each candidate TID together with
!  * the active seed, and then selecting it if the hash is less than the
!  * cutoff value computed from the selection probability by BeginSampleScan.
!  *
!  *
!  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
!  * Portions Copyright (c) 1994, Regents of the University of California
   *
   * IDENTIFICATION
!  *      src/backend/access/tablesample/bernoulli.c
   *
   *-------------------------------------------------------------------------
   */

  #include "postgres.h"

! #ifdef _MSC_VER
! #include <float.h>                /* for _isnan */
! #endif
! #include <math.h>

! #include "access/hash.h"
! #include "access/tsmapi.h"
! #include "catalog/pg_type.h"
  #include "optimizer/clauses.h"
! #include "optimizer/cost.h"
! #include "utils/builtins.h"


! /* Private state */
  typedef struct
  {
+     uint64        cutoff;            /* select tuples with hash less than this */
      uint32        seed;            /* random seed */
      OffsetNumber lt;            /* last tuple returned from current block */
  } BernoulliSamplerData;

+
+ static void bernoulli_samplescancost(PlannerInfo *root,
+                          RelOptInfo *baserel,
+                          List *paramexprs,
+                          BlockNumber *pages,
+                          double *tuples);
+ static void bernoulli_initsamplescan(SampleScanState *node,
+                          int eflags);
+ static void bernoulli_beginsamplescan(SampleScanState *node,
+                           Datum *params,
+                           int nparams,
+                           uint32 seed);
+ static OffsetNumber bernoulli_nextsampletuple(SampleScanState *node,
+                           BlockNumber blockno,
+                           OffsetNumber maxoffset);
+
+
  /*
!  * Create a TsmRoutine descriptor for the BERNOULLI method.
   */
  Datum
! tsm_bernoulli_handler(PG_FUNCTION_ARGS)
  {
!     TsmRoutine *tsm = makeNode(TsmRoutine);

!     tsm->parameterTypes = list_make1_oid(FLOAT4OID);
!     tsm->repeatable_across_queries = true;
!     tsm->repeatable_across_scans = true;
!     tsm->SampleScanCost = bernoulli_samplescancost;
!     tsm->InitSampleScan = bernoulli_initsamplescan;
!     tsm->BeginSampleScan = bernoulli_beginsamplescan;
!     tsm->NextSampleBlock = NULL;
!     tsm->NextSampleTuple = bernoulli_nextsampletuple;
!     tsm->EndSampleScan = NULL;

!     PG_RETURN_POINTER(tsm);
  }

  /*
!  * Cost estimation.
   */
! static void
! bernoulli_samplescancost(PlannerInfo *root,
!                          RelOptInfo *baserel,
!                          List *paramexprs,
!                          BlockNumber *pages,
!                          double *tuples)
  {
!     Node       *pctnode;
!     float4        samplesize;

!     /* Try to extract an estimate for the sample percentage */
!     pctnode = (Node *) linitial(paramexprs);
!     pctnode = estimate_expression_value(root, pctnode);
!
!     if (IsA(pctnode, Const) &&
!         !((Const *) pctnode)->constisnull)
!     {
!         samplesize = DatumGetFloat4(((Const *) pctnode)->constvalue);
!         if (samplesize >= 0 && samplesize <= 100 && !isnan(samplesize))
!             samplesize /= 100.0f;
!         else
!         {
!             /* Default samplesize if the value is bogus */
!             samplesize = 0.1f;
!         }
!     }
      else
      {
!         /* Default samplesize if the estimation didn't return Const */
!         samplesize = 0.1f;
!     }

!     /* We'll visit all pages of the baserel */
!     *pages = baserel->pages;

!     *tuples = clamp_row_est(baserel->tuples * samplesize);
! }

! /*
!  * Initialize during executor setup.
!  */
! static void
! bernoulli_initsamplescan(SampleScanState *node, int eflags)
! {
!     node->tsm_state = palloc0(sizeof(BernoulliSamplerData));
  }

  /*
!  * Examine parameters and prepare for a sample scan.
!  */
! static void
! bernoulli_beginsamplescan(SampleScanState *node,
!                           Datum *params,
!                           int nparams,
!                           uint32 seed)
! {
!     BernoulliSamplerData *sampler = (BernoulliSamplerData *) node->tsm_state;
!     double        percent = DatumGetFloat4(params[0]);
!
!     if (percent < 0 || percent > 100 || isnan(percent))
!         ereport(ERROR,
!                 (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
!                  errmsg("invalid sample size"),
!            errdetail("Sample size must be between 0 and 100 (inclusive).")));
!
!     /*
!      * The cutoff is sample probability times (PG_UINT32_MAX + 1); we have to
!      * store that as a uint64, of course.  Note that this gives strictly
!      * correct behavior at the limits of zero or one probability.
!      */
!     sampler->cutoff = rint(((double) PG_UINT32_MAX + 1) * percent / 100);
!     sampler->seed = seed;
!     sampler->lt = InvalidOffsetNumber;
! }
!
! /*
!  * Select next sampled tuple in current block.
   *
!  * It is OK here to return an offset without knowing if the tuple is visible
!  * (or even exists).  The reason is that we do the coinflip for every tuple
!  * offset in the table.  Since all tuples have the same probability of being
!  * returned, it doesn't matter if we do extra coinflips for invisible tuples.
   *
!  * When we reach end of the block, return InvalidOffsetNumber which tells
   * SampleScan to go to next block.
   */
! static OffsetNumber
! bernoulli_nextsampletuple(SampleScanState *node,
!                           BlockNumber blockno,
!                           OffsetNumber maxoffset)
  {
!     BernoulliSamplerData *sampler = (BernoulliSamplerData *) node->tsm_state;
      OffsetNumber tupoffset = sampler->lt;
!     uint32        hashinput[3];

+     /* Advance to first/next tuple in block */
      if (tupoffset == InvalidOffsetNumber)
          tupoffset = FirstOffsetNumber;
      else
          tupoffset++;

      /*
!      * We compute the hash by applying hash_any to an array of 3 uint32's
!      * containing the block, offset, and seed.  This is efficient to set up,
!      * and with the current implementation of hash_any, it gives
!      * machine-independent results, which is a nice property for regression
!      * testing.
       *
!      * These words in the hash input are the same throughout the block:
       */
!     hashinput[0] = blockno;
!     hashinput[2] = sampler->seed;
!
!     /*
!      * Loop over tuple offsets until finding suitable TID or reaching end of
!      * block.
!      */
!     for (; tupoffset <= maxoffset; tupoffset++)
      {
!         uint32        hash;

!         hashinput[1] = tupoffset;
!
!         hash = DatumGetUInt32(hash_any((const unsigned char *) hashinput,
!                                        (int) sizeof(hashinput)));
!         if (hash < sampler->cutoff)
              break;
      }

      if (tupoffset > maxoffset)
          tupoffset = InvalidOffsetNumber;

      sampler->lt = tupoffset;

!     return tupoffset;
  }
diff --git a/src/backend/access/tablesample/system.c b/src/backend/access/tablesample/system.c
index 1d83436..7cece29 100644
*** a/src/backend/access/tablesample/system.c
--- b/src/backend/access/tablesample/system.c
***************
*** 1,186 ****
  /*-------------------------------------------------------------------------
   *
   * system.c
!  *      interface routines for system tablesample method
   *
   *
!  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
   *
   * IDENTIFICATION
!  *      src/backend/utils/tablesample/system.c
   *
   *-------------------------------------------------------------------------
   */

  #include "postgres.h"

! #include "fmgr.h"

! #include "access/tablesample.h"
  #include "access/relscan.h"
! #include "nodes/execnodes.h"
! #include "nodes/relation.h"
  #include "optimizer/clauses.h"
! #include "storage/bufmgr.h"
! #include "utils/sampling.h"


! /*
!  * State
!  */
  typedef struct
  {
!     BlockSamplerData bs;
      uint32        seed;            /* random seed */
!     BlockNumber nblocks;        /* number of block in relation */
!     int            samplesize;        /* number of blocks to return */
      OffsetNumber lt;            /* last tuple returned from current block */
  } SystemSamplerData;


! /*
!  * Initializes the state.
!  */
! Datum
! tsm_system_init(PG_FUNCTION_ARGS)
! {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     uint32        seed = PG_GETARG_UINT32(1);
!     float4        percent = PG_ARGISNULL(2) ? -1 : PG_GETARG_FLOAT4(2);
!     HeapScanDesc scan = tsdesc->heapScan;
!     SystemSamplerData *sampler;
!
!     if (percent < 0 || percent > 100)
!         ereport(ERROR,
!                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
!                  errmsg("invalid sample size"),
!                  errhint("Sample size must be numeric value between 0 and 100 (inclusive).")));
!
!     sampler = palloc0(sizeof(SystemSamplerData));
!
!     /* Remember initial values for reinit */
!     sampler->seed = seed;
!     sampler->nblocks = scan->rs_nblocks;
!     sampler->samplesize = 1 + (int) (sampler->nblocks * (percent / 100.0));
!     sampler->lt = InvalidOffsetNumber;
!
!     BlockSampler_Init(&sampler->bs, sampler->nblocks, sampler->samplesize,
!                       sampler->seed);
!
!     tsdesc->tsmdata = (void *) sampler;

-     PG_RETURN_VOID();
- }

  /*
!  * Get next block number or InvalidBlockNumber when we're done.
!  *
!  * Uses the same logic as ANALYZE for picking the random blocks.
   */
  Datum
! tsm_system_nextblock(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;
!     BlockNumber blockno;
!
!     if (!BlockSampler_HasMore(&sampler->bs))
!         PG_RETURN_UINT32(InvalidBlockNumber);

!     blockno = BlockSampler_Next(&sampler->bs);

!     PG_RETURN_UINT32(blockno);
  }

  /*
!  * Get next tuple offset in current block or InvalidOffsetNumber if we are done
!  * with this block.
   */
! Datum
! tsm_system_nexttuple(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     OffsetNumber maxoffset = PG_GETARG_UINT16(2);
!     SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;
!     OffsetNumber tupoffset = sampler->lt;

!     if (tupoffset == InvalidOffsetNumber)
!         tupoffset = FirstOffsetNumber;
!     else
!         tupoffset++;

!     if (tupoffset > maxoffset)
!         tupoffset = InvalidOffsetNumber;

!     sampler->lt = tupoffset;

!     PG_RETURN_UINT16(tupoffset);
  }

  /*
!  * Cleanup method.
   */
! Datum
! tsm_system_end(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!
!     pfree(tsdesc->tsmdata);
!
!     PG_RETURN_VOID();
  }

  /*
!  * Reset state (called by ReScan).
   */
! Datum
! tsm_system_reset(PG_FUNCTION_ARGS)
  {
!     TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
!     SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;

!     sampler->lt = InvalidOffsetNumber;
!     BlockSampler_Init(&sampler->bs, sampler->nblocks, sampler->samplesize,
!                       sampler->seed);

!     PG_RETURN_VOID();
  }

  /*
!  * Costing function.
   */
! Datum
! tsm_system_cost(PG_FUNCTION_ARGS)
  {
!     PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
!     Path       *path = (Path *) PG_GETARG_POINTER(1);
!     RelOptInfo *baserel = (RelOptInfo *) PG_GETARG_POINTER(2);
!     List       *args = (List *) PG_GETARG_POINTER(3);
!     BlockNumber *pages = (BlockNumber *) PG_GETARG_POINTER(4);
!     double       *tuples = (double *) PG_GETARG_POINTER(5);
!     Node       *pctnode;
!     float4        samplesize;
!
!     pctnode = linitial(args);
!     pctnode = estimate_expression_value(root, pctnode);

!     if (IsA(pctnode, RelabelType))
!         pctnode = (Node *) ((RelabelType *) pctnode)->arg;

!     if (IsA(pctnode, Const))
      {
!         samplesize = DatumGetFloat4(((Const *) pctnode)->constvalue);
!         samplesize /= 100.0;
      }
!     else
      {
!         /* Default samplesize if the estimation didn't return Const. */
!         samplesize = 0.1f;
      }

!     *pages = baserel->pages * samplesize;
!     *tuples = path->rows * samplesize;
!     path->rows = *tuples;

!     PG_RETURN_VOID();
  }
--- 1,250 ----
  /*-------------------------------------------------------------------------
   *
   * system.c
!  *      support routines for SYSTEM tablesample method
   *
+  * To ensure repeatability of samples, it is necessary that selection of a
+  * given tuple be history-independent; otherwise syncscanning would break
+  * repeatability, to say nothing of logically-irrelevant maintenance such
+  * as physical extension or shortening of the relation.
   *
!  * To achieve that, we proceed by hashing each candidate block number together
!  * with the active seed, and then selecting it if the hash is less than the
!  * cutoff value computed from the selection probability by BeginSampleScan.
!  *
!  *
!  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
!  * Portions Copyright (c) 1994, Regents of the University of California
   *
   * IDENTIFICATION
!  *      src/backend/access/tablesample/system.c
   *
   *-------------------------------------------------------------------------
   */

  #include "postgres.h"

! #ifdef _MSC_VER
! #include <float.h>                /* for _isnan */
! #endif
! #include <math.h>

! #include "access/hash.h"
  #include "access/relscan.h"
! #include "access/tsmapi.h"
! #include "catalog/pg_type.h"
  #include "optimizer/clauses.h"
! #include "optimizer/cost.h"
! #include "utils/builtins.h"


! /* Private state */
  typedef struct
  {
!     uint64        cutoff;            /* select blocks with hash less than this */
      uint32        seed;            /* random seed */
!     BlockNumber nextblock;        /* next block to consider sampling */
      OffsetNumber lt;            /* last tuple returned from current block */
  } SystemSamplerData;


! static void system_samplescancost(PlannerInfo *root,
!                       RelOptInfo *baserel,
!                       List *paramexprs,
!                       BlockNumber *pages,
!                       double *tuples);
! static void system_initsamplescan(SampleScanState *node,
!                       int eflags);
! static void system_beginsamplescan(SampleScanState *node,
!                        Datum *params,
!                        int nparams,
!                        uint32 seed);
! static BlockNumber system_nextsampleblock(SampleScanState *node);
! static OffsetNumber system_nextsampletuple(SampleScanState *node,
!                        BlockNumber blockno,
!                        OffsetNumber maxoffset);


  /*
!  * Create a TsmRoutine descriptor for the SYSTEM method.
   */
  Datum
! tsm_system_handler(PG_FUNCTION_ARGS)
  {
!     TsmRoutine *tsm = makeNode(TsmRoutine);

!     tsm->parameterTypes = list_make1_oid(FLOAT4OID);
!     tsm->repeatable_across_queries = true;
!     tsm->repeatable_across_scans = true;
!     tsm->SampleScanCost = system_samplescancost;
!     tsm->InitSampleScan = system_initsamplescan;
!     tsm->BeginSampleScan = system_beginsamplescan;
!     tsm->NextSampleBlock = system_nextsampleblock;
!     tsm->NextSampleTuple = system_nextsampletuple;
!     tsm->EndSampleScan = NULL;

!     PG_RETURN_POINTER(tsm);
  }

  /*
!  * Cost estimation.
   */
! static void
! system_samplescancost(PlannerInfo *root,
!                       RelOptInfo *baserel,
!                       List *paramexprs,
!                       BlockNumber *pages,
!                       double *tuples)
  {
!     Node       *pctnode;
!     float4        samplesize;

!     /* Try to extract an estimate for the sample percentage */
!     pctnode = (Node *) linitial(paramexprs);
!     pctnode = estimate_expression_value(root, pctnode);

!     if (IsA(pctnode, Const) &&
!         !((Const *) pctnode)->constisnull)
!     {
!         samplesize = DatumGetFloat4(((Const *) pctnode)->constvalue);
!         if (samplesize >= 0 && samplesize <= 100 && !isnan(samplesize))
!             samplesize /= 100.0f;
!         else
!         {
!             /* Default samplesize if the value is bogus */
!             samplesize = 0.1f;
!         }
!     }
!     else
!     {
!         /* Default samplesize if the estimation didn't return Const */
!         samplesize = 0.1f;
!     }

!     /* We'll visit a sample of the pages ... */
!     *pages = baserel->pages * samplesize;

!     /* ... and hopefully get a representative number of tuples from them */
!     *tuples = clamp_row_est(baserel->tuples * samplesize);
  }

  /*
!  * Initialize during executor setup.
   */
! static void
! system_initsamplescan(SampleScanState *node, int eflags)
  {
!     node->tsm_state = palloc0(sizeof(SystemSamplerData));
  }

  /*
!  * Examine parameters and prepare for a sample scan.
   */
! static void
! system_beginsamplescan(SampleScanState *node,
!                        Datum *params,
!                        int nparams,
!                        uint32 seed)
  {
!     SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
!     double        percent = DatumGetFloat4(params[0]);

!     if (percent < 0 || percent > 100 || isnan(percent))
!         ereport(ERROR,
!                 (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
!                  errmsg("invalid sample size"),
!            errdetail("Sample size must be between 0 and 100 (inclusive).")));

!     /*
!      * The cutoff is sample probability times (PG_UINT32_MAX + 1); we have to
!      * store that as a uint64, of course.  Note that this gives strictly
!      * correct behavior at the limits of zero or one probability.
!      */
!     sampler->cutoff = rint(((double) PG_UINT32_MAX + 1) * percent / 100);
!     sampler->seed = seed;
!     sampler->nextblock = 0;
!     sampler->lt = InvalidOffsetNumber;
  }

  /*
!  * Select next block to sample.
   */
! static BlockNumber
! system_nextsampleblock(SampleScanState *node)
  {
!     SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
!     HeapScanDesc scan = node->ss.ss_currentScanDesc;
!     BlockNumber nextblock = sampler->nextblock;
!     uint32        hashinput[2];

!     /*
!      * We compute the hash by applying hash_any to an array of 2 uint32's
!      * containing the block number and seed.  This is efficient to set up, and
!      * with the current implementation of hash_any, it gives
!      * machine-independent results, which is a nice property for regression
!      * testing.
!      *
!      * These words in the hash input are the same throughout the block:
!      */
!     hashinput[1] = sampler->seed;

!     /*
!      * Loop over block numbers until finding suitable block or reaching end of
!      * relation.
!      */
!     for (; nextblock < scan->rs_nblocks; nextblock++)
      {
!         uint32        hash;
!
!         hashinput[0] = nextblock;
!
!         hash = DatumGetUInt32(hash_any((const unsigned char *) hashinput,
!                                        (int) sizeof(hashinput)));
!         if (hash < sampler->cutoff)
!             break;
      }
!
!     if (nextblock < scan->rs_nblocks)
      {
!         /* Found a suitable block; remember where we should start next time */
!         sampler->nextblock = nextblock + 1;
!         return nextblock;
      }

!     /* Done, but let's reset nextblock to 0 for safety. */
!     sampler->nextblock = 0;
!     return InvalidBlockNumber;
! }

! /*
!  * Select next sampled tuple in current block.
!  *
!  * In block sampling, we just want to sample all the tuples in each selected
!  * block.
!  *
!  * It is OK here to return an offset without knowing if the tuple is visible
!  * (or even exists); nodeSamplescan.c will deal with that.
!  *
!  * When we reach end of the block, return InvalidOffsetNumber which tells
!  * SampleScan to go to next block.
!  */
! static OffsetNumber
! system_nextsampletuple(SampleScanState *node,
!                        BlockNumber blockno,
!                        OffsetNumber maxoffset)
! {
!     SystemSamplerData *sampler = (SystemSamplerData *) node->tsm_state;
!     OffsetNumber tupoffset = sampler->lt;
!
!     if (tupoffset == InvalidOffsetNumber)
!         tupoffset = FirstOffsetNumber;
!     else
!         tupoffset++;
!
!     if (tupoffset > maxoffset)
!         tupoffset = InvalidOffsetNumber;
!
!     sampler->lt = tupoffset;
!
!     return tupoffset;
  }
diff --git a/src/backend/access/tablesample/tablesample.c b/src/backend/access/tablesample/tablesample.c
index f21d42c..b8ad7ce 100644
*** a/src/backend/access/tablesample/tablesample.c
--- b/src/backend/access/tablesample/tablesample.c
***************
*** 1,7 ****
  /*-------------------------------------------------------------------------
   *
   * tablesample.c
!  *          TABLESAMPLE internal API
   *
   * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
--- 1,7 ----
  /*-------------------------------------------------------------------------
   *
   * tablesample.c
!  *          Support functions for TABLESAMPLE feature
   *
   * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
***************
*** 10,365 ****
   * IDENTIFICATION
   *          src/backend/access/tablesample/tablesample.c
   *
-  * TABLESAMPLE is the SQL standard clause for sampling the relations.
-  *
-  * The API is interface between the Executor and the TABLESAMPLE Methods.
-  *
-  * TABLESAMPLE Methods are implementations of actual sampling algorithms which
-  * can be used for returning a sample of the source relation.
-  * Methods don't read the table directly but are asked for block number and
-  * tuple offset which they want to examine (or return) and the tablesample
-  * interface implemented here does the reading for them.
-  *
-  * We currently only support sampling of the physical relations, but in the
-  * future we might extend the API to support subqueries as well.
-  *
   * -------------------------------------------------------------------------
   */

  #include "postgres.h"

! #include "access/tablesample.h"
!
! #include "catalog/pg_tablesample_method.h"
! #include "miscadmin.h"
! #include "pgstat.h"
! #include "storage/bufmgr.h"
! #include "storage/predicate.h"
! #include "utils/rel.h"
! #include "utils/tqual.h"
!
!
! static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan);
!
!
! /*
!  * Initialize the TABLESAMPLE Descriptor and the TABLESAMPLE Method.
!  */
! TableSampleDesc *
! tablesample_init(SampleScanState *scanstate, TableSampleClause *tablesample)
! {
!     FunctionCallInfoData fcinfo;
!     int            i;
!     List       *args = tablesample->args;
!     ListCell   *arg;
!     ExprContext *econtext = scanstate->ss.ps.ps_ExprContext;
!     TableSampleDesc *tsdesc = (TableSampleDesc *) palloc0(sizeof(TableSampleDesc));
!
!     /* Load functions */
!     fmgr_info(tablesample->tsminit, &(tsdesc->tsminit));
!     fmgr_info(tablesample->tsmnextblock, &(tsdesc->tsmnextblock));
!     fmgr_info(tablesample->tsmnexttuple, &(tsdesc->tsmnexttuple));
!     if (OidIsValid(tablesample->tsmexaminetuple))
!         fmgr_info(tablesample->tsmexaminetuple, &(tsdesc->tsmexaminetuple));
!     else
!         tsdesc->tsmexaminetuple.fn_oid = InvalidOid;
!     fmgr_info(tablesample->tsmreset, &(tsdesc->tsmreset));
!     fmgr_info(tablesample->tsmend, &(tsdesc->tsmend));
!
!     InitFunctionCallInfoData(fcinfo, &tsdesc->tsminit,
!                              list_length(args) + 2,
!                              InvalidOid, NULL, NULL);
!
!     tsdesc->tupDesc = scanstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
!     tsdesc->heapScan = scanstate->ss.ss_currentScanDesc;
!
!     /* First argument for init function is always TableSampleDesc */
!     fcinfo.arg[0] = PointerGetDatum(tsdesc);
!     fcinfo.argnull[0] = false;
!
!     /*
!      * Second arg for init function is always REPEATABLE.
!      *
!      * If tablesample->repeatable is NULL then REPEATABLE clause was not
!      * specified, and we insert a random value as default.
!      *
!      * When specified, the expression cannot evaluate to NULL.
!      */
!     if (tablesample->repeatable)
!     {
!         ExprState  *argstate = ExecInitExpr((Expr *) tablesample->repeatable,
!                                             (PlanState *) scanstate);
!
!         fcinfo.arg[1] = ExecEvalExpr(argstate, econtext,
!                                      &fcinfo.argnull[1], NULL);
!         if (fcinfo.argnull[1])
!             ereport(ERROR,
!                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
!                 errmsg("REPEATABLE clause must be NOT NULL numeric value")));
!     }
!     else
!     {
!         fcinfo.arg[1] = UInt32GetDatum(random());
!         fcinfo.argnull[1] = false;
!     }
!
!     /* Rest of the arguments come from user. */
!     i = 2;
!     foreach(arg, args)
!     {
!         Expr       *argexpr = (Expr *) lfirst(arg);
!         ExprState  *argstate = ExecInitExpr(argexpr, (PlanState *) scanstate);
!
!         fcinfo.arg[i] = ExecEvalExpr(argstate, econtext,
!                                      &fcinfo.argnull[i], NULL);
!         i++;
!     }
!     Assert(i == fcinfo.nargs);
!
!     (void) FunctionCallInvoke(&fcinfo);
!
!     return tsdesc;
! }
!
! /*
!  * Get next tuple from TABLESAMPLE Method.
!  */
! HeapTuple
! tablesample_getnext(TableSampleDesc *desc)
! {
!     HeapScanDesc scan = desc->heapScan;
!     HeapTuple    tuple = &(scan->rs_ctup);
!     bool        pagemode = scan->rs_pageatatime;
!     BlockNumber blockno;
!     Page        page;
!     bool        page_all_visible;
!     ItemId        itemid;
!     OffsetNumber tupoffset,
!                 maxoffset;
!
!     if (!scan->rs_inited)
!     {
!         /*
!          * return null immediately if relation is empty
!          */
!         if (scan->rs_nblocks == 0)
!         {
!             Assert(!BufferIsValid(scan->rs_cbuf));
!             tuple->t_data = NULL;
!             return NULL;
!         }
!         blockno = DatumGetInt32(FunctionCall1(&desc->tsmnextblock,
!                                               PointerGetDatum(desc)));
!         if (!BlockNumberIsValid(blockno))
!         {
!             tuple->t_data = NULL;
!             return NULL;
!         }
!
!         heapgetpage(scan, blockno);
!         scan->rs_inited = true;
!     }
!     else
!     {
!         /* continue from previously returned page/tuple */
!         blockno = scan->rs_cblock;        /* current page */
!     }
!
!     /*
!      * When pagemode is disabled, the scan will do visibility checks for each
!      * tuple it finds so the buffer needs to be locked.
!      */
!     if (!pagemode)
!         LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
!
!     page = (Page) BufferGetPage(scan->rs_cbuf);
!     page_all_visible = PageIsAllVisible(page);
!     maxoffset = PageGetMaxOffsetNumber(page);
!
!     for (;;)
!     {
!         CHECK_FOR_INTERRUPTS();
!
!         tupoffset = DatumGetUInt16(FunctionCall3(&desc->tsmnexttuple,
!                                                  PointerGetDatum(desc),
!                                                  UInt32GetDatum(blockno),
!                                                  UInt16GetDatum(maxoffset)));
!
!         if (OffsetNumberIsValid(tupoffset))
!         {
!             bool        visible;
!             bool        found;
!
!             /* Skip invalid tuple pointers. */
!             itemid = PageGetItemId(page, tupoffset);
!             if (!ItemIdIsNormal(itemid))
!                 continue;
!
!             tuple->t_data = (HeapTupleHeader) PageGetItem((Page) page, itemid);
!             tuple->t_len = ItemIdGetLength(itemid);
!             ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
!
!             if (page_all_visible)
!                 visible = true;
!             else
!                 visible = SampleTupleVisible(tuple, tupoffset, scan);
!
!             /*
!              * Let the sampling method examine the actual tuple and decide if
!              * we should return it.
!              *
!              * Note that we let it examine even invisible tuples for
!              * statistical purposes, but not return them since user should
!              * never see invisible tuples.
!              */
!             if (OidIsValid(desc->tsmexaminetuple.fn_oid))
!             {
!                 found = DatumGetBool(FunctionCall4(&desc->tsmexaminetuple,
!                                                    PointerGetDatum(desc),
!                                                    UInt32GetDatum(blockno),
!                                                    PointerGetDatum(tuple),
!                                                    BoolGetDatum(visible)));
!                 /* Should not happen if sampling method is well written. */
!                 if (found && !visible)
!                     elog(ERROR, "Sampling method wanted to return invisible tuple");
!             }
!             else
!                 found = visible;
!
!             /* Found visible tuple, return it. */
!             if (found)
!             {
!                 if (!pagemode)
!                     LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
!                 break;
!             }
!             else
!             {
!                 /* Try next tuple from same page. */
!                 continue;
!             }
!         }
!
!
!         if (!pagemode)
!             LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
!
!         blockno = DatumGetInt32(FunctionCall1(&desc->tsmnextblock,
!                                               PointerGetDatum(desc)));
!
!         /*
!          * Report our new scan position for synchronization purposes. We don't
!          * do that when moving backwards, however. That would just mess up any
!          * other forward-moving scanners.
!          *
!          * Note: we do this before checking for end of scan so that the final
!          * state of the position hint is back at the start of the rel.  That's
!          * not strictly necessary, but otherwise when you run the same query
!          * multiple times the starting position would shift a little bit
!          * backwards on every invocation, which is confusing. We don't
!          * guarantee any specific ordering in general, though.
!          */
!         if (scan->rs_syncscan)
!             ss_report_location(scan->rs_rd, BlockNumberIsValid(blockno) ?
!                                blockno : scan->rs_startblock);
!
!         /*
!          * Reached end of scan.
!          */
!         if (!BlockNumberIsValid(blockno))
!         {
!             if (BufferIsValid(scan->rs_cbuf))
!                 ReleaseBuffer(scan->rs_cbuf);
!             scan->rs_cbuf = InvalidBuffer;
!             scan->rs_cblock = InvalidBlockNumber;
!             tuple->t_data = NULL;
!             scan->rs_inited = false;
!             return NULL;
!         }
!
!         heapgetpage(scan, blockno);
!
!         if (!pagemode)
!             LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
!
!         page = (Page) BufferGetPage(scan->rs_cbuf);
!         page_all_visible = PageIsAllVisible(page);
!         maxoffset = PageGetMaxOffsetNumber(page);
!     }
!
!     pgstat_count_heap_getnext(scan->rs_rd);
!
!     return &(scan->rs_ctup);
! }
!
! /*
!  * Reset the sampling to starting state
!  */
! void
! tablesample_reset(TableSampleDesc *desc)
! {
!     (void) FunctionCall1(&desc->tsmreset, PointerGetDatum(desc));
! }

- /*
-  * Signal the sampling method that the scan has finished.
-  */
- void
- tablesample_end(TableSampleDesc *desc)
- {
-     (void) FunctionCall1(&desc->tsmend, PointerGetDatum(desc));
- }

  /*
!  * Check visibility of the tuple.
   */
! static bool
! SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
  {
!     /*
!      * If this scan is reading whole pages at a time, there is already
!      * visibility info present in rs_vistuples so we can just search it for
!      * the tupoffset.
!      */
!     if (scan->rs_pageatatime)
!     {
!         int            start = 0,
!                     end = scan->rs_ntuples - 1;
!
!         /*
!          * Do the binary search over rs_vistuples, it's already sorted by
!          * OffsetNumber so we don't need to do any sorting ourselves here.
!          *
!          * We could use bsearch() here but it's slower for integers because of
!          * the function call overhead and because it needs boiler plate code
!          * it would not save us anything code-wise anyway.
!          */
!         while (start <= end)
!         {
!             int            mid = start + (end - start) / 2;
!             OffsetNumber curoffset = scan->rs_vistuples[mid];
!
!             if (curoffset == tupoffset)
!                 return true;
!             else if (curoffset > tupoffset)
!                 end = mid - 1;
!             else
!                 start = mid + 1;
!         }
!
!         return false;
!     }
!     else
!     {
!         /* No pagemode, we have to check the tuple itself. */
!         Snapshot    snapshot = scan->rs_snapshot;
!         Buffer        buffer = scan->rs_cbuf;

!         bool        visible = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer);

!         CheckForSerializableConflictOut(visible, scan->rs_rd, tuple, buffer,
!                                         snapshot);

!         return visible;
!     }
  }
--- 10,40 ----
   * IDENTIFICATION
   *          src/backend/access/tablesample/tablesample.c
   *
   * -------------------------------------------------------------------------
   */

  #include "postgres.h"

! #include "access/tsmapi.h"


  /*
!  * GetTsmRoutine --- get a TsmRoutine struct by invoking the handler.
!  *
!  * This is a convenience routine that's just meant to check for errors.
   */
! TsmRoutine *
! GetTsmRoutine(Oid tsmhandler)
  {
!     Datum        datum;
!     TsmRoutine *routine;

!     datum = OidFunctionCall1(tsmhandler, PointerGetDatum(NULL));
!     routine = (TsmRoutine *) DatumGetPointer(datum);

!     if (routine == NULL || !IsA(routine, TsmRoutine))
!         elog(ERROR, "tablesample handler function %u did not return a TsmRoutine struct",
!              tsmhandler);

!     return routine;
  }
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 3d1139b..25130ec 100644
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
*** 40,47 ****
      pg_ts_parser.h pg_ts_template.h pg_extension.h \
      pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
      pg_foreign_table.h pg_policy.h pg_replication_origin.h \
!     pg_tablesample_method.h pg_default_acl.h pg_seclabel.h pg_shseclabel.h \
!     pg_collation.h pg_range.h pg_transform.h toasting.h indexing.h \
      )

  # location of Catalog.pm
--- 40,48 ----
      pg_ts_parser.h pg_ts_template.h pg_extension.h \
      pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
      pg_foreign_table.h pg_policy.h pg_replication_origin.h \
!     pg_default_acl.h pg_seclabel.h pg_shseclabel.h \
!     pg_collation.h pg_range.h pg_transform.h \
!     toasting.h indexing.h \
      )

  # location of Catalog.pm
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 5d7c441..90b1cd8 100644
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
*************** find_expr_references_walker(Node *node,
*** 1911,1916 ****
--- 1911,1924 ----
                                     context->addrs);
          }
      }
+     else if (IsA(node, TableSampleClause))
+     {
+         TableSampleClause *tsc = (TableSampleClause *) node;
+
+         add_object_address(OCLASS_PROC, tsc->tsmhandler, 0,
+                            context->addrs);
+         /* fall through to examine arguments */
+     }

      return expression_tree_walker(node, find_expr_references_walker,
                                    (void *) context);
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 0d1ecc2..5d06fa4 100644
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
*************** static void show_sort_group_keys(PlanSta
*** 96,101 ****
--- 96,103 ----
                       List *ancestors, ExplainState *es);
  static void show_sortorder_options(StringInfo buf, Node *sortexpr,
                         Oid sortOperator, Oid collation, bool nullsFirst);
+ static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
+                  List *ancestors, ExplainState *es);
  static void show_sort_info(SortState *sortstate, ExplainState *es);
  static void show_hash_info(HashState *hashstate, ExplainState *es);
  static void show_tidbitmap_info(BitmapHeapScanState *planstate,
*************** ExplainPreScanNode(PlanState *planstate,
*** 730,735 ****
--- 732,738 ----
      switch (nodeTag(plan))
      {
          case T_SeqScan:
+         case T_SampleScan:
          case T_IndexScan:
          case T_IndexOnlyScan:
          case T_BitmapHeapScan:
*************** ExplainPreScanNode(PlanState *planstate,
*** 739,745 ****
          case T_ValuesScan:
          case T_CteScan:
          case T_WorkTableScan:
-         case T_SampleScan:
              *rels_used = bms_add_member(*rels_used,
                                          ((Scan *) plan)->scanrelid);
              break;
--- 742,747 ----
*************** ExplainNode(PlanState *planstate, List *
*** 935,940 ****
--- 937,945 ----
          case T_SeqScan:
              pname = sname = "Seq Scan";
              break;
+         case T_SampleScan:
+             pname = sname = "Sample Scan";
+             break;
          case T_IndexScan:
              pname = sname = "Index Scan";
              break;
*************** ExplainNode(PlanState *planstate, List *
*** 976,998 ****
              else
                  pname = sname;
              break;
-         case T_SampleScan:
-             {
-                 /*
-                  * Fetch the tablesample method name from RTE.
-                  *
-                  * It would be nice to also show parameters, but since we
-                  * support arbitrary expressions as parameter it might get
-                  * quite messy.
-                  */
-                 RangeTblEntry *rte;
-
-                 rte = rt_fetch(((SampleScan *) plan)->scanrelid, es->rtable);
-                 custom_name = get_tablesample_method_name(rte->tablesample->tsmid);
-                 pname = psprintf("Sample Scan (%s)", custom_name);
-                 sname = "Sample Scan";
-             }
-             break;
          case T_Material:
              pname = sname = "Materialize";
              break;
--- 981,986 ----
*************** ExplainNode(PlanState *planstate, List *
*** 1101,1106 ****
--- 1089,1095 ----
      switch (nodeTag(plan))
      {
          case T_SeqScan:
+         case T_SampleScan:
          case T_BitmapHeapScan:
          case T_TidScan:
          case T_SubqueryScan:
*************** ExplainNode(PlanState *planstate, List *
*** 1115,1123 ****
              if (((Scan *) plan)->scanrelid > 0)
                  ExplainScanTarget((Scan *) plan, es);
              break;
-         case T_SampleScan:
-             ExplainScanTarget((Scan *) plan, es);
-             break;
          case T_IndexScan:
              {
                  IndexScan  *indexscan = (IndexScan *) plan;
--- 1104,1109 ----
*************** ExplainNode(PlanState *planstate, List *
*** 1363,1374 ****
              if (es->analyze)
                  show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
              break;
          case T_SeqScan:
          case T_ValuesScan:
          case T_CteScan:
          case T_WorkTableScan:
          case T_SubqueryScan:
-         case T_SampleScan:
              show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
              if (plan->qual)
                  show_instrumentation_count("Rows Removed by Filter", 1,
--- 1349,1363 ----
              if (es->analyze)
                  show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
              break;
+         case T_SampleScan:
+             show_tablesample(((SampleScan *) plan)->tablesample,
+                              planstate, ancestors, es);
+             /* FALL THRU to print additional fields the same as SeqScan */
          case T_SeqScan:
          case T_ValuesScan:
          case T_CteScan:
          case T_WorkTableScan:
          case T_SubqueryScan:
              show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
              if (plan->qual)
                  show_instrumentation_count("Rows Removed by Filter", 1,
*************** show_sortorder_options(StringInfo buf, N
*** 2110,2115 ****
--- 2099,2170 ----
  }

  /*
+  * Show TABLESAMPLE properties
+  */
+ static void
+ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
+                  List *ancestors, ExplainState *es)
+ {
+     List       *context;
+     bool        useprefix;
+     char       *method_name;
+     List       *params = NIL;
+     char       *repeatable;
+     ListCell   *lc;
+
+     /* Set up deparsing context */
+     context = set_deparse_context_planstate(es->deparse_cxt,
+                                             (Node *) planstate,
+                                             ancestors);
+     useprefix = list_length(es->rtable) > 1;
+
+     /* Get the tablesample method name */
+     method_name = get_func_name(tsc->tsmhandler);
+
+     /* Deparse parameter expressions */
+     foreach(lc, tsc->args)
+     {
+         Node       *arg = (Node *) lfirst(lc);
+
+         params = lappend(params,
+                          deparse_expression(arg, context,
+                                             useprefix, false));
+     }
+     if (tsc->repeatable)
+         repeatable = deparse_expression((Node *) tsc->repeatable, context,
+                                         useprefix, false);
+     else
+         repeatable = NULL;
+
+     /* Print results */
+     if (es->format == EXPLAIN_FORMAT_TEXT)
+     {
+         bool        first = true;
+
+         appendStringInfoSpaces(es->str, es->indent * 2);
+         appendStringInfo(es->str, "Sampling: %s (", method_name);
+         foreach(lc, params)
+         {
+             if (!first)
+                 appendStringInfoString(es->str, ", ");
+             appendStringInfoString(es->str, (const char *) lfirst(lc));
+             first = false;
+         }
+         appendStringInfoChar(es->str, ')');
+         if (repeatable)
+             appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
+         appendStringInfoChar(es->str, '\n');
+     }
+     else
+     {
+         ExplainPropertyText("Sampling Method", method_name, es);
+         ExplainPropertyList("Sampling Parameters", params, es);
+         if (repeatable)
+             ExplainPropertyText("Repeatable Seed", repeatable, es);
+     }
+ }
+
+ /*
   * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
   */
  static void
*************** ExplainTargetRel(Plan *plan, Index rti,
*** 2366,2378 ****
      switch (nodeTag(plan))
      {
          case T_SeqScan:
          case T_IndexScan:
          case T_IndexOnlyScan:
          case T_BitmapHeapScan:
          case T_TidScan:
          case T_ForeignScan:
          case T_CustomScan:
-         case T_SampleScan:
          case T_ModifyTable:
              /* Assert it's on a real relation */
              Assert(rte->rtekind == RTE_RELATION);
--- 2421,2433 ----
      switch (nodeTag(plan))
      {
          case T_SeqScan:
+         case T_SampleScan:
          case T_IndexScan:
          case T_IndexOnlyScan:
          case T_BitmapHeapScan:
          case T_TidScan:
          case T_ForeignScan:
          case T_CustomScan:
          case T_ModifyTable:
              /* Assert it's on a real relation */
              Assert(rte->rtekind == RTE_RELATION);
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 04073d3..93e1e9a 100644
*** a/src/backend/executor/execAmi.c
--- b/src/backend/executor/execAmi.c
*************** ExecSupportsBackwardScan(Plan *node)
*** 463,468 ****
--- 463,472 ----
          case T_CteScan:
              return TargetListSupportsBackwardScan(node->targetlist);

+         case T_SampleScan:
+             /* Simplify life for tablesample methods by disallowing this */
+             return false;
+
          case T_IndexScan:
              return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
                  TargetListSupportsBackwardScan(node->targetlist);
*************** ExecSupportsBackwardScan(Plan *node)
*** 485,493 ****
              }
              return false;

-         case T_SampleScan:
-             return false;
-
          case T_Material:
          case T_Sort:
              /* these don't evaluate tlist */
--- 489,494 ----
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 4c1c523..32db05e 100644
*** a/src/backend/executor/nodeSamplescan.c
--- b/src/backend/executor/nodeSamplescan.c
***************
*** 3,9 ****
   * nodeSamplescan.c
   *      Support routines for sample scans of relations (table sampling).
   *
!  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
--- 3,9 ----
   * nodeSamplescan.c
   *      Support routines for sample scans of relations (table sampling).
   *
!  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
***************
*** 14,35 ****
   */
  #include "postgres.h"

! #include "access/tablesample.h"
  #include "executor/executor.h"
  #include "executor/nodeSamplescan.h"
  #include "miscadmin.h"
- #include "parser/parsetree.h"
  #include "pgstat.h"
  #include "storage/bufmgr.h"
  #include "storage/predicate.h"
  #include "utils/rel.h"
- #include "utils/syscache.h"
  #include "utils/tqual.h"

! static void InitScanRelation(SampleScanState *node, EState *estate,
!                  int eflags, TableSampleClause *tablesample);
  static TupleTableSlot *SampleNext(SampleScanState *node);
!

  /* ----------------------------------------------------------------
   *                        Scan Support
--- 14,37 ----
   */
  #include "postgres.h"

! #include "access/hash.h"
! #include "access/relscan.h"
! #include "access/tsmapi.h"
  #include "executor/executor.h"
  #include "executor/nodeSamplescan.h"
  #include "miscadmin.h"
  #include "pgstat.h"
  #include "storage/bufmgr.h"
  #include "storage/predicate.h"
  #include "utils/rel.h"
  #include "utils/tqual.h"

! static void InitScanRelation(SampleScanState *node, EState *estate, int eflags);
  static TupleTableSlot *SampleNext(SampleScanState *node);
! static void tablesample_init(SampleScanState *scanstate);
! static HeapTuple tablesample_getnext(SampleScanState *scanstate);
! static bool SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset,
!                    HeapScanDesc scan);

  /* ----------------------------------------------------------------
   *                        Scan Support
*************** static TupleTableSlot *SampleNext(Sample
*** 45,67 ****
  static TupleTableSlot *
  SampleNext(SampleScanState *node)
  {
-     TupleTableSlot *slot;
-     TableSampleDesc *tsdesc;
      HeapTuple    tuple;

      /*
!      * get information from the scan state
       */
!     slot = node->ss.ss_ScanTupleSlot;
!     tsdesc = node->tsdesc;

!     tuple = tablesample_getnext(tsdesc);

      if (tuple)
          ExecStoreTuple(tuple,    /* tuple to store */
                         slot,    /* slot to store in */
!                        tsdesc->heapScan->rs_cbuf,        /* buffer associated
!                                                          * with this tuple */
                         false);    /* don't pfree this pointer */
      else
          ExecClearTuple(slot);
--- 47,72 ----
  static TupleTableSlot *
  SampleNext(SampleScanState *node)
  {
      HeapTuple    tuple;
+     TupleTableSlot *slot;

      /*
!      * if this is first call within a scan, initialize
       */
!     if (!node->begun)
!         tablesample_init(node);

!     /*
!      * get the next tuple, and store it in our result slot
!      */
!     tuple = tablesample_getnext(node);
!
!     slot = node->ss.ss_ScanTupleSlot;

      if (tuple)
          ExecStoreTuple(tuple,    /* tuple to store */
                         slot,    /* slot to store in */
!                        node->ss.ss_currentScanDesc->rs_cbuf,    /* tuple's buffer */
                         false);    /* don't pfree this pointer */
      else
          ExecClearTuple(slot);
*************** SampleNext(SampleScanState *node)
*** 75,81 ****
  static bool
  SampleRecheck(SampleScanState *node, TupleTableSlot *slot)
  {
!     /* No need to recheck for SampleScan */
      return true;
  }

--- 80,89 ----
  static bool
  SampleRecheck(SampleScanState *node, TupleTableSlot *slot)
  {
!     /*
!      * No need to recheck for SampleScan, since like SeqScan we don't pass any
!      * checkable keys to heap_beginscan.
!      */
      return true;
  }

*************** ExecSampleScan(SampleScanState *node)
*** 103,110 ****
   * ----------------------------------------------------------------
   */
  static void
! InitScanRelation(SampleScanState *node, EState *estate, int eflags,
!                  TableSampleClause *tablesample)
  {
      Relation    currentRelation;

--- 111,117 ----
   * ----------------------------------------------------------------
   */
  static void
! InitScanRelation(SampleScanState *node, EState *estate, int eflags)
  {
      Relation    currentRelation;

*************** InitScanRelation(SampleScanState *node,
*** 113,131 ****
       * open that relation and acquire appropriate lock on it.
       */
      currentRelation = ExecOpenScanRelation(estate,
!                                 ((SampleScan *) node->ss.ps.plan)->scanrelid,
                                             eflags);

      node->ss.ss_currentRelation = currentRelation;

!     /*
!      * Even though we aren't going to do a conventional seqscan, it is useful
!      * to create a HeapScanDesc --- many of the fields in it are usable.
!      */
!     node->ss.ss_currentScanDesc =
!         heap_beginscan_sampling(currentRelation, estate->es_snapshot, 0, NULL,
!                                 tablesample->tsmseqscan,
!                                 tablesample->tsmpagemode);

      /* and report the scan tuple slot's rowtype */
      ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
--- 120,132 ----
       * open that relation and acquire appropriate lock on it.
       */
      currentRelation = ExecOpenScanRelation(estate,
!                            ((SampleScan *) node->ss.ps.plan)->scan.scanrelid,
                                             eflags);

      node->ss.ss_currentRelation = currentRelation;

!     /* we won't set up the HeapScanDesc till later */
!     node->ss.ss_currentScanDesc = NULL;

      /* and report the scan tuple slot's rowtype */
      ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
*************** SampleScanState *
*** 140,151 ****
  ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
  {
      SampleScanState *scanstate;
!     RangeTblEntry *rte = rt_fetch(node->scanrelid,
!                                   estate->es_range_table);

      Assert(outerPlan(node) == NULL);
      Assert(innerPlan(node) == NULL);
-     Assert(rte->tablesample != NULL);

      /*
       * create state structure
--- 141,151 ----
  ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
  {
      SampleScanState *scanstate;
!     TableSampleClause *tsc = node->tablesample;
!     TsmRoutine *tsm;

      Assert(outerPlan(node) == NULL);
      Assert(innerPlan(node) == NULL);

      /*
       * create state structure
*************** ExecInitSampleScan(SampleScan *node, ESt
*** 165,174 ****
       * initialize child expressions
       */
      scanstate->ss.ps.targetlist = (List *)
!         ExecInitExpr((Expr *) node->plan.targetlist,
                       (PlanState *) scanstate);
      scanstate->ss.ps.qual = (List *)
!         ExecInitExpr((Expr *) node->plan.qual,
                       (PlanState *) scanstate);

      /*
--- 165,181 ----
       * initialize child expressions
       */
      scanstate->ss.ps.targetlist = (List *)
!         ExecInitExpr((Expr *) node->scan.plan.targetlist,
                       (PlanState *) scanstate);
      scanstate->ss.ps.qual = (List *)
!         ExecInitExpr((Expr *) node->scan.plan.qual,
!                      (PlanState *) scanstate);
!
!     scanstate->args = (List *)
!         ExecInitExpr((Expr *) tsc->args,
!                      (PlanState *) scanstate);
!     scanstate->repeatable =
!         ExecInitExpr(tsc->repeatable,
                       (PlanState *) scanstate);

      /*
*************** ExecInitSampleScan(SampleScan *node, ESt
*** 180,186 ****
      /*
       * initialize scan relation
       */
!     InitScanRelation(scanstate, estate, eflags, rte->tablesample);

      scanstate->ss.ps.ps_TupFromTlist = false;

--- 187,193 ----
      /*
       * initialize scan relation
       */
!     InitScanRelation(scanstate, estate, eflags);

      scanstate->ss.ps.ps_TupFromTlist = false;

*************** ExecInitSampleScan(SampleScan *node, ESt
*** 190,196 ****
      ExecAssignResultTypeFromTL(&scanstate->ss.ps);
      ExecAssignScanProjectionInfo(&scanstate->ss);

!     scanstate->tsdesc = tablesample_init(scanstate, rte->tablesample);

      return scanstate;
  }
--- 197,221 ----
      ExecAssignResultTypeFromTL(&scanstate->ss.ps);
      ExecAssignScanProjectionInfo(&scanstate->ss);

!     /*
!      * If we don't have a REPEATABLE clause, select a random seed.  We want to
!      * do this just once, since the seed shouldn't change over rescans.
!      */
!     if (tsc->repeatable == NULL)
!         scanstate->seed = random();
!
!     /*
!      * Finally, initialize the TABLESAMPLE method handler.
!      */
!     tsm = GetTsmRoutine(tsc->tsmhandler);
!     scanstate->tsmroutine = tsm;
!     scanstate->tsm_state = NULL;
!
!     if (tsm->InitSampleScan)
!         tsm->InitSampleScan(scanstate, eflags);
!
!     /* We'll do BeginSampleScan later; we can't evaluate params yet */
!     scanstate->begun = false;

      return scanstate;
  }
*************** ExecEndSampleScan(SampleScanState *node)
*** 207,213 ****
      /*
       * Tell sampling function that we finished the scan.
       */
!     tablesample_end(node->tsdesc);

      /*
       * Free the exprcontext
--- 232,239 ----
      /*
       * Tell sampling function that we finished the scan.
       */
!     if (node->tsmroutine->EndSampleScan)
!         node->tsmroutine->EndSampleScan(node);

      /*
       * Free the exprcontext
*************** ExecEndSampleScan(SampleScanState *node)
*** 223,229 ****
      /*
       * close heap scan
       */
!     heap_endscan(node->ss.ss_currentScanDesc);

      /*
       * close the heap relation.
--- 249,256 ----
      /*
       * close heap scan
       */
!     if (node->ss.ss_currentScanDesc)
!         heap_endscan(node->ss.ss_currentScanDesc);

      /*
       * close the heap relation.
*************** ExecEndSampleScan(SampleScanState *node)
*** 232,242 ****
  }

  /* ----------------------------------------------------------------
-  *                        Join Support
-  * ----------------------------------------------------------------
-  */
-
- /* ----------------------------------------------------------------
   *        ExecReScanSampleScan
   *
   *        Rescans the relation.
--- 259,264 ----
*************** ExecEndSampleScan(SampleScanState *node)
*** 246,257 ****
  void
  ExecReScanSampleScan(SampleScanState *node)
  {
!     heap_rescan(node->ss.ss_currentScanDesc, NULL);

      /*
!      * Tell sampling function to reset its state for rescan.
       */
!     tablesample_reset(node->tsdesc);

!     ExecScanReScan(&node->ss);
  }
--- 268,596 ----
  void
  ExecReScanSampleScan(SampleScanState *node)
  {
!     /* Remember we need to do BeginSampleScan again (if we did it at all) */
!     node->begun = false;
!
!     ExecScanReScan(&node->ss);
! }
!
!
! /*
!  * Initialize the TABLESAMPLE method: evaluate params and call BeginSampleScan.
!  */
! static void
! tablesample_init(SampleScanState *scanstate)
! {
!     TsmRoutine *tsm = scanstate->tsmroutine;
!     ExprContext *econtext = scanstate->ss.ps.ps_ExprContext;
!     Datum       *params;
!     Datum        datum;
!     bool        isnull;
!     uint32        seed;
!     bool        allow_sync;
!     int            i;
!     ListCell   *arg;
!
!     params = (Datum *) palloc(list_length(scanstate->args) * sizeof(Datum));
!
!     i = 0;
!     foreach(arg, scanstate->args)
!     {
!         ExprState  *argstate = (ExprState *) lfirst(arg);
!
!         params[i] = ExecEvalExprSwitchContext(argstate,
!                                               econtext,
!                                               &isnull,
!                                               NULL);
!         if (isnull)
!             ereport(ERROR,
!                     (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
!                      errmsg("TABLESAMPLE parameter cannot be null")));
!         i++;
!     }
!
!     if (scanstate->repeatable)
!     {
!         datum = ExecEvalExprSwitchContext(scanstate->repeatable,
!                                           econtext,
!                                           &isnull,
!                                           NULL);
!         if (isnull)
!             ereport(ERROR,
!                     (errcode(ERRCODE_INVALID_TABLESAMPLE_REPEAT),
!                  errmsg("TABLESAMPLE REPEATABLE parameter cannot be null")));
!
!         /*
!          * We use hashfloat8() to convert the supplied float8 value into a
!          * suitable seed.  For regression-testing purposes, that has the
!          * convenient property that REPEATABLE(0) gives a machine-independent
!          * result.
!          */
!         seed = DatumGetUInt32(DirectFunctionCall1(hashfloat8, datum));
!     }
!     else
!     {
!         /* Use the seed selected by ExecInitSampleScan */
!         seed = scanstate->seed;
!     }
!
!     /* Set default values for params that BeginSampleScan can adjust */
!     scanstate->use_bulkread = true;
!     scanstate->use_pagemode = true;
!
!     /* Let tablesample method do its thing */
!     tsm->BeginSampleScan(scanstate,
!                          params,
!                          list_length(scanstate->args),
!                          seed);
!
!     /* We'll use syncscan if there's no NextSampleBlock function */
!     allow_sync = (tsm->NextSampleBlock == NULL);
!
!     /* Now we can create or reset the HeapScanDesc */
!     if (scanstate->ss.ss_currentScanDesc == NULL)
!     {
!         scanstate->ss.ss_currentScanDesc =
!             heap_beginscan_sampling(scanstate->ss.ss_currentRelation,
!                                     scanstate->ss.ps.state->es_snapshot,
!                                     0, NULL,
!                                     scanstate->use_bulkread,
!                                     allow_sync,
!                                     scanstate->use_pagemode);
!     }
!     else
!     {
!         heap_rescan_set_params(scanstate->ss.ss_currentScanDesc, NULL,
!                                scanstate->use_bulkread,
!                                allow_sync,
!                                scanstate->use_pagemode);
!     }
!
!     pfree(params);
!
!     /* And we're initialized. */
!     scanstate->begun = true;
! }
!
! /*
!  * Get next tuple from TABLESAMPLE method.
!  *
!  * Note: an awful lot of this is copied-and-pasted from heapam.c.  It would
!  * perhaps be better to refactor to share more code.
!  */
! static HeapTuple
! tablesample_getnext(SampleScanState *scanstate)
! {
!     TsmRoutine *tsm = scanstate->tsmroutine;
!     HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
!     HeapTuple    tuple = &(scan->rs_ctup);
!     Snapshot    snapshot = scan->rs_snapshot;
!     bool        pagemode = scan->rs_pageatatime;
!     BlockNumber blockno;
!     Page        page;
!     bool        all_visible;
!     OffsetNumber maxoffset;
!
!     if (!scan->rs_inited)
!     {
!         /*
!          * return null immediately if relation is empty
!          */
!         if (scan->rs_nblocks == 0)
!         {
!             Assert(!BufferIsValid(scan->rs_cbuf));
!             tuple->t_data = NULL;
!             return NULL;
!         }
!         if (tsm->NextSampleBlock)
!         {
!             blockno = tsm->NextSampleBlock(scanstate);
!             if (!BlockNumberIsValid(blockno))
!             {
!                 tuple->t_data = NULL;
!                 return NULL;
!             }
!         }
!         else
!             blockno = scan->rs_startblock;
!         Assert(blockno < scan->rs_nblocks);
!         heapgetpage(scan, blockno);
!         scan->rs_inited = true;
!     }
!     else
!     {
!         /* continue from previously returned page/tuple */
!         blockno = scan->rs_cblock;        /* current page */
!     }

      /*
!      * When not using pagemode, we must lock the buffer during tuple
!      * visibility checks.
       */
!     if (!pagemode)
!         LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);

!     page = (Page) BufferGetPage(scan->rs_cbuf);
!     all_visible = PageIsAllVisible(page) && !snapshot->takenDuringRecovery;
!     maxoffset = PageGetMaxOffsetNumber(page);
!
!     for (;;)
!     {
!         OffsetNumber tupoffset;
!         bool        finished;
!
!         CHECK_FOR_INTERRUPTS();
!
!         /* Ask the tablesample method which tuples to check on this page. */
!         tupoffset = tsm->NextSampleTuple(scanstate,
!                                          blockno,
!                                          maxoffset);
!
!         if (OffsetNumberIsValid(tupoffset))
!         {
!             ItemId        itemid;
!             bool        visible;
!
!             /* Skip invalid tuple pointers. */
!             itemid = PageGetItemId(page, tupoffset);
!             if (!ItemIdIsNormal(itemid))
!                 continue;
!
!             tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
!             tuple->t_len = ItemIdGetLength(itemid);
!             ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
!
!             if (all_visible)
!                 visible = true;
!             else
!                 visible = SampleTupleVisible(tuple, tupoffset, scan);
!
!             /* in pagemode, heapgetpage did this for us */
!             if (!pagemode)
!                 CheckForSerializableConflictOut(visible, scan->rs_rd, tuple,
!                                                 scan->rs_cbuf, snapshot);
!
!             if (visible)
!             {
!                 /* Found visible tuple, return it. */
!                 if (!pagemode)
!                     LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
!                 break;
!             }
!             else
!             {
!                 /* Try next tuple from same page. */
!                 continue;
!             }
!         }
!
!         /*
!          * if we get here, it means we've exhausted the items on this page and
!          * it's time to move to the next.
!          */
!         if (!pagemode)
!             LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
!
!         if (tsm->NextSampleBlock)
!         {
!             blockno = tsm->NextSampleBlock(scanstate);
!             Assert(!scan->rs_syncscan);
!             finished = !BlockNumberIsValid(blockno);
!         }
!         else
!         {
!             /* Without NextSampleBlock, just do a plain forward seqscan. */
!             blockno++;
!             if (blockno >= scan->rs_nblocks)
!                 blockno = 0;
!
!             /*
!              * Report our new scan position for synchronization purposes.
!              *
!              * Note: we do this before checking for end of scan so that the
!              * final state of the position hint is back at the start of the
!              * rel.  That's not strictly necessary, but otherwise when you run
!              * the same query multiple times the starting position would shift
!              * a little bit backwards on every invocation, which is confusing.
!              * We don't guarantee any specific ordering in general, though.
!              */
!             if (scan->rs_syncscan)
!                 ss_report_location(scan->rs_rd, blockno);
!
!             finished = (blockno == scan->rs_startblock);
!         }
!
!         /*
!          * Reached end of scan?
!          */
!         if (finished)
!         {
!             if (BufferIsValid(scan->rs_cbuf))
!                 ReleaseBuffer(scan->rs_cbuf);
!             scan->rs_cbuf = InvalidBuffer;
!             scan->rs_cblock = InvalidBlockNumber;
!             tuple->t_data = NULL;
!             scan->rs_inited = false;
!             return NULL;
!         }
!
!         heapgetpage(scan, blockno);
!
!         /* Re-establish state for new page */
!         if (!pagemode)
!             LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
!
!         page = (Page) BufferGetPage(scan->rs_cbuf);
!         all_visible = PageIsAllVisible(page) && !snapshot->takenDuringRecovery;
!         maxoffset = PageGetMaxOffsetNumber(page);
!     }
!
!     /* Count successfully-fetched tuples as heap fetches */
!     pgstat_count_heap_getnext(scan->rs_rd);
!
!     return &(scan->rs_ctup);
! }
!
! /*
!  * Check visibility of the tuple.
!  */
! static bool
! SampleTupleVisible(HeapTuple tuple, OffsetNumber tupoffset, HeapScanDesc scan)
! {
!     if (scan->rs_pageatatime)
!     {
!         /*
!          * In pageatatime mode, heapgetpage() already did visibility checks,
!          * so just look at the info it left in rs_vistuples[].
!          *
!          * We use a binary search over the known-sorted array.  Note: we could
!          * save some effort if we insisted that NextSampleTuple select tuples
!          * in increasing order, but it's not clear that there would be enough
!          * gain to justify the restriction.
!          */
!         int            start = 0,
!                     end = scan->rs_ntuples - 1;
!
!         while (start <= end)
!         {
!             int            mid = (start + end) / 2;
!             OffsetNumber curoffset = scan->rs_vistuples[mid];
!
!             if (tupoffset == curoffset)
!                 return true;
!             else if (tupoffset < curoffset)
!                 end = mid - 1;
!             else
!                 start = mid + 1;
!         }
!
!         return false;
!     }
!     else
!     {
!         /* Otherwise, we have to check the tuple individually. */
!         return HeapTupleSatisfiesVisibility(tuple,
!                                             scan->rs_snapshot,
!                                             scan->rs_cbuf);
!     }
  }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6a08c2d..7248440 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copySeqScan(const SeqScan *from)
*** 360,365 ****
--- 360,386 ----
  }

  /*
+  * _copySampleScan
+  */
+ static SampleScan *
+ _copySampleScan(const SampleScan *from)
+ {
+     SampleScan *newnode = makeNode(SampleScan);
+
+     /*
+      * copy node superclass fields
+      */
+     CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+     /*
+      * copy remainder of node
+      */
+     COPY_NODE_FIELD(tablesample);
+
+     return newnode;
+ }
+
+ /*
   * _copyIndexScan
   */
  static IndexScan *
*************** _copyCustomScan(const CustomScan *from)
*** 642,663 ****
  }

  /*
-  * _copySampleScan
-  */
- static SampleScan *
- _copySampleScan(const SampleScan *from)
- {
-     SampleScan *newnode = makeNode(SampleScan);
-
-     /*
-      * copy node superclass fields
-      */
-     CopyScanFields((const Scan *) from, (Scan *) newnode);
-
-     return newnode;
- }
-
- /*
   * CopyJoinFields
   *
   *        This function copies the fields of the Join node.  It is used by
--- 663,668 ----
*************** _copyRangeTblFunction(const RangeTblFunc
*** 2143,2148 ****
--- 2148,2165 ----
      return newnode;
  }

+ static TableSampleClause *
+ _copyTableSampleClause(const TableSampleClause *from)
+ {
+     TableSampleClause *newnode = makeNode(TableSampleClause);
+
+     COPY_SCALAR_FIELD(tsmhandler);
+     COPY_NODE_FIELD(args);
+     COPY_NODE_FIELD(repeatable);
+
+     return newnode;
+ }
+
  static WithCheckOption *
  _copyWithCheckOption(const WithCheckOption *from)
  {
*************** _copyCommonTableExpr(const CommonTableEx
*** 2271,2310 ****
      return newnode;
  }

- static RangeTableSample *
- _copyRangeTableSample(const RangeTableSample *from)
- {
-     RangeTableSample *newnode = makeNode(RangeTableSample);
-
-     COPY_NODE_FIELD(relation);
-     COPY_STRING_FIELD(method);
-     COPY_NODE_FIELD(repeatable);
-     COPY_NODE_FIELD(args);
-
-     return newnode;
- }
-
- static TableSampleClause *
- _copyTableSampleClause(const TableSampleClause *from)
- {
-     TableSampleClause *newnode = makeNode(TableSampleClause);
-
-     COPY_SCALAR_FIELD(tsmid);
-     COPY_SCALAR_FIELD(tsmseqscan);
-     COPY_SCALAR_FIELD(tsmpagemode);
-     COPY_SCALAR_FIELD(tsminit);
-     COPY_SCALAR_FIELD(tsmnextblock);
-     COPY_SCALAR_FIELD(tsmnexttuple);
-     COPY_SCALAR_FIELD(tsmexaminetuple);
-     COPY_SCALAR_FIELD(tsmend);
-     COPY_SCALAR_FIELD(tsmreset);
-     COPY_SCALAR_FIELD(tsmcost);
-     COPY_NODE_FIELD(repeatable);
-     COPY_NODE_FIELD(args);
-
-     return newnode;
- }
-
  static A_Expr *
  _copyAExpr(const A_Expr *from)
  {
--- 2288,2293 ----
*************** _copyRangeFunction(const RangeFunction *
*** 2532,2537 ****
--- 2515,2534 ----
      return newnode;
  }

+ static RangeTableSample *
+ _copyRangeTableSample(const RangeTableSample *from)
+ {
+     RangeTableSample *newnode = makeNode(RangeTableSample);
+
+     COPY_NODE_FIELD(relation);
+     COPY_NODE_FIELD(method);
+     COPY_NODE_FIELD(args);
+     COPY_NODE_FIELD(repeatable);
+     COPY_LOCATION_FIELD(location);
+
+     return newnode;
+ }
+
  static TypeCast *
  _copyTypeCast(const TypeCast *from)
  {
*************** copyObject(const void *from)
*** 4237,4242 ****
--- 4234,4242 ----
          case T_SeqScan:
              retval = _copySeqScan(from);
              break;
+         case T_SampleScan:
+             retval = _copySampleScan(from);
+             break;
          case T_IndexScan:
              retval = _copyIndexScan(from);
              break;
*************** copyObject(const void *from)
*** 4273,4281 ****
          case T_CustomScan:
              retval = _copyCustomScan(from);
              break;
-         case T_SampleScan:
-             retval = _copySampleScan(from);
-             break;
          case T_Join:
              retval = _copyJoin(from);
              break;
--- 4273,4278 ----
*************** copyObject(const void *from)
*** 4897,4902 ****
--- 4894,4902 ----
          case T_RangeFunction:
              retval = _copyRangeFunction(from);
              break;
+         case T_RangeTableSample:
+             retval = _copyRangeTableSample(from);
+             break;
          case T_TypeName:
              retval = _copyTypeName(from);
              break;
*************** copyObject(const void *from)
*** 4921,4926 ****
--- 4921,4929 ----
          case T_RangeTblFunction:
              retval = _copyRangeTblFunction(from);
              break;
+         case T_TableSampleClause:
+             retval = _copyTableSampleClause(from);
+             break;
          case T_WithCheckOption:
              retval = _copyWithCheckOption(from);
              break;
*************** copyObject(const void *from)
*** 4948,4959 ****
          case T_CommonTableExpr:
              retval = _copyCommonTableExpr(from);
              break;
-         case T_RangeTableSample:
-             retval = _copyRangeTableSample(from);
-             break;
-         case T_TableSampleClause:
-             retval = _copyTableSampleClause(from);
-             break;
          case T_FuncWithArgs:
              retval = _copyFuncWithArgs(from);
              break;
--- 4951,4956 ----
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index faf5eed..6597dbc 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalRangeFunction(const RangeFunction
*** 2291,2296 ****
--- 2291,2308 ----
  }

  static bool
+ _equalRangeTableSample(const RangeTableSample *a, const RangeTableSample *b)
+ {
+     COMPARE_NODE_FIELD(relation);
+     COMPARE_NODE_FIELD(method);
+     COMPARE_NODE_FIELD(args);
+     COMPARE_NODE_FIELD(repeatable);
+     COMPARE_LOCATION_FIELD(location);
+
+     return true;
+ }
+
+ static bool
  _equalIndexElem(const IndexElem *a, const IndexElem *b)
  {
      COMPARE_STRING_FIELD(name);
*************** _equalRangeTblFunction(const RangeTblFun
*** 2429,2434 ****
--- 2441,2456 ----
  }

  static bool
+ _equalTableSampleClause(const TableSampleClause *a, const TableSampleClause *b)
+ {
+     COMPARE_SCALAR_FIELD(tsmhandler);
+     COMPARE_NODE_FIELD(args);
+     COMPARE_NODE_FIELD(repeatable);
+
+     return true;
+ }
+
+ static bool
  _equalWithCheckOption(const WithCheckOption *a, const WithCheckOption *b)
  {
      COMPARE_SCALAR_FIELD(kind);
*************** _equalCommonTableExpr(const CommonTableE
*** 2539,2574 ****
  }

  static bool
- _equalRangeTableSample(const RangeTableSample *a, const RangeTableSample *b)
- {
-     COMPARE_NODE_FIELD(relation);
-     COMPARE_STRING_FIELD(method);
-     COMPARE_NODE_FIELD(repeatable);
-     COMPARE_NODE_FIELD(args);
-
-     return true;
- }
-
- static bool
- _equalTableSampleClause(const TableSampleClause *a, const TableSampleClause *b)
- {
-     COMPARE_SCALAR_FIELD(tsmid);
-     COMPARE_SCALAR_FIELD(tsmseqscan);
-     COMPARE_SCALAR_FIELD(tsmpagemode);
-     COMPARE_SCALAR_FIELD(tsminit);
-     COMPARE_SCALAR_FIELD(tsmnextblock);
-     COMPARE_SCALAR_FIELD(tsmnexttuple);
-     COMPARE_SCALAR_FIELD(tsmexaminetuple);
-     COMPARE_SCALAR_FIELD(tsmend);
-     COMPARE_SCALAR_FIELD(tsmreset);
-     COMPARE_SCALAR_FIELD(tsmcost);
-     COMPARE_NODE_FIELD(repeatable);
-     COMPARE_NODE_FIELD(args);
-
-     return true;
- }
-
- static bool
  _equalXmlSerialize(const XmlSerialize *a, const XmlSerialize *b)
  {
      COMPARE_SCALAR_FIELD(xmloption);
--- 2561,2566 ----
*************** equal(const void *a, const void *b)
*** 3260,3265 ****
--- 3252,3260 ----
          case T_RangeFunction:
              retval = _equalRangeFunction(a, b);
              break;
+         case T_RangeTableSample:
+             retval = _equalRangeTableSample(a, b);
+             break;
          case T_TypeName:
              retval = _equalTypeName(a, b);
              break;
*************** equal(const void *a, const void *b)
*** 3284,3289 ****
--- 3279,3287 ----
          case T_RangeTblFunction:
              retval = _equalRangeTblFunction(a, b);
              break;
+         case T_TableSampleClause:
+             retval = _equalTableSampleClause(a, b);
+             break;
          case T_WithCheckOption:
              retval = _equalWithCheckOption(a, b);
              break;
*************** equal(const void *a, const void *b)
*** 3311,3322 ****
          case T_CommonTableExpr:
              retval = _equalCommonTableExpr(a, b);
              break;
-         case T_RangeTableSample:
-             retval = _equalRangeTableSample(a, b);
-             break;
-         case T_TableSampleClause:
-             retval = _equalTableSampleClause(a, b);
-             break;
          case T_FuncWithArgs:
              retval = _equalFuncWithArgs(a, b);
              break;
--- 3309,3314 ----
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index b1e3e6e..c517dfd 100644
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** exprLocation(const Node *expr)
*** 1486,1491 ****
--- 1486,1494 ----
          case T_WindowDef:
              loc = ((const WindowDef *) expr)->location;
              break;
+         case T_RangeTableSample:
+             loc = ((const RangeTableSample *) expr)->location;
+             break;
          case T_TypeName:
              loc = ((const TypeName *) expr)->location;
              break;
*************** expression_tree_walker(Node *node,
*** 1995,2000 ****
--- 1998,2014 ----
              return walker(((PlaceHolderInfo *) node)->ph_var, context);
          case T_RangeTblFunction:
              return walker(((RangeTblFunction *) node)->funcexpr, context);
+         case T_TableSampleClause:
+             {
+                 TableSampleClause *tsc = (TableSampleClause *) node;
+
+                 if (expression_tree_walker((Node *) tsc->args,
+                                            walker, context))
+                     return true;
+                 if (walker((Node *) tsc->repeatable, context))
+                     return true;
+             }
+             break;
          default:
              elog(ERROR, "unrecognized node type: %d",
                   (int) nodeTag(node));
*************** range_table_walker(List *rtable,
*** 2082,2094 ****
          switch (rte->rtekind)
          {
              case RTE_RELATION:
!                 if (rte->tablesample)
!                 {
!                     if (walker(rte->tablesample->args, context))
!                         return true;
!                     if (walker(rte->tablesample->repeatable, context))
!                         return true;
!                 }
                  break;
              case RTE_CTE:
                  /* nothing to do */
--- 2096,2103 ----
          switch (rte->rtekind)
          {
              case RTE_RELATION:
!                 if (walker(rte->tablesample, context))
!                     return true;
                  break;
              case RTE_CTE:
                  /* nothing to do */
*************** expression_tree_mutator(Node *node,
*** 2782,2787 ****
--- 2791,2807 ----
                  return (Node *) newnode;
              }
              break;
+         case T_TableSampleClause:
+             {
+                 TableSampleClause *tsc = (TableSampleClause *) node;
+                 TableSampleClause *newnode;
+
+                 FLATCOPY(newnode, tsc, TableSampleClause);
+                 MUTATE(newnode->args, tsc->args, List *);
+                 MUTATE(newnode->repeatable, tsc->repeatable, Expr *);
+                 return (Node *) newnode;
+             }
+             break;
          default:
              elog(ERROR, "unrecognized node type: %d",
                   (int) nodeTag(node));
*************** range_table_mutator(List *rtable,
*** 2868,2887 ****
          switch (rte->rtekind)
          {
              case RTE_RELATION:
!                 if (rte->tablesample)
!                 {
!                     CHECKFLATCOPY(newrte->tablesample, rte->tablesample,
!                                   TableSampleClause);
!                     MUTATE(newrte->tablesample->args,
!                            newrte->tablesample->args,
!                            List *);
!                     MUTATE(newrte->tablesample->repeatable,
!                            newrte->tablesample->repeatable,
!                            Node *);
!                 }
                  break;
              case RTE_CTE:
!                 /* we don't bother to copy eref, aliases, etc; OK? */
                  break;
              case RTE_SUBQUERY:
                  if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
--- 2888,2899 ----
          switch (rte->rtekind)
          {
              case RTE_RELATION:
!                 MUTATE(newrte->tablesample, rte->tablesample,
!                        TableSampleClause *);
!                 /* we don't bother to copy eref, aliases, etc; OK? */
                  break;
              case RTE_CTE:
!                 /* nothing to do */
                  break;
              case RTE_SUBQUERY:
                  if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
*************** raw_expression_tree_walker(Node *node,
*** 3316,3321 ****
--- 3328,3346 ----
                      return true;
              }
              break;
+         case T_RangeTableSample:
+             {
+                 RangeTableSample *rts = (RangeTableSample *) node;
+
+                 if (walker(rts->relation, context))
+                     return true;
+                 /* method name is deemed uninteresting */
+                 if (walker(rts->args, context))
+                     return true;
+                 if (walker(rts->repeatable, context))
+                     return true;
+             }
+             break;
          case T_TypeName:
              {
                  TypeName   *tn = (TypeName *) node;
*************** raw_expression_tree_walker(Node *node,
*** 3380,3397 ****
              break;
          case T_CommonTableExpr:
              return walker(((CommonTableExpr *) node)->ctequery, context);
-         case T_RangeTableSample:
-             {
-                 RangeTableSample *rts = (RangeTableSample *) node;
-
-                 if (walker(rts->relation, context))
-                     return true;
-                 if (walker(rts->repeatable, context))
-                     return true;
-                 if (walker(rts->args, context))
-                     return true;
-             }
-             break;
          default:
              elog(ERROR, "unrecognized node type: %d",
                   (int) nodeTag(node));
--- 3405,3410 ----
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 87304ba..81725d6 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outSeqScan(StringInfo str, const SeqSca
*** 445,450 ****
--- 445,460 ----
  }

  static void
+ _outSampleScan(StringInfo str, const SampleScan *node)
+ {
+     WRITE_NODE_TYPE("SAMPLESCAN");
+
+     _outScanInfo(str, (const Scan *) node);
+
+     WRITE_NODE_FIELD(tablesample);
+ }
+
+ static void
  _outIndexScan(StringInfo str, const IndexScan *node)
  {
      WRITE_NODE_TYPE("INDEXSCAN");
*************** _outCustomScan(StringInfo str, const Cus
*** 592,605 ****
  }

  static void
- _outSampleScan(StringInfo str, const SampleScan *node)
- {
-     WRITE_NODE_TYPE("SAMPLESCAN");
-
-     _outScanInfo(str, (const Scan *) node);
- }
-
- static void
  _outJoin(StringInfo str, const Join *node)
  {
      WRITE_NODE_TYPE("JOIN");
--- 602,607 ----
*************** _outCommonTableExpr(StringInfo str, cons
*** 2479,2514 ****
  }

  static void
- _outRangeTableSample(StringInfo str, const RangeTableSample *node)
- {
-     WRITE_NODE_TYPE("RANGETABLESAMPLE");
-
-     WRITE_NODE_FIELD(relation);
-     WRITE_STRING_FIELD(method);
-     WRITE_NODE_FIELD(repeatable);
-     WRITE_NODE_FIELD(args);
- }
-
- static void
- _outTableSampleClause(StringInfo str, const TableSampleClause *node)
- {
-     WRITE_NODE_TYPE("TABLESAMPLECLAUSE");
-
-     WRITE_OID_FIELD(tsmid);
-     WRITE_BOOL_FIELD(tsmseqscan);
-     WRITE_BOOL_FIELD(tsmpagemode);
-     WRITE_OID_FIELD(tsminit);
-     WRITE_OID_FIELD(tsmnextblock);
-     WRITE_OID_FIELD(tsmnexttuple);
-     WRITE_OID_FIELD(tsmexaminetuple);
-     WRITE_OID_FIELD(tsmend);
-     WRITE_OID_FIELD(tsmreset);
-     WRITE_OID_FIELD(tsmcost);
-     WRITE_NODE_FIELD(repeatable);
-     WRITE_NODE_FIELD(args);
- }
-
- static void
  _outSetOperationStmt(StringInfo str, const SetOperationStmt *node)
  {
      WRITE_NODE_TYPE("SETOPERATIONSTMT");
--- 2481,2486 ----
*************** _outRangeTblFunction(StringInfo str, con
*** 2595,2600 ****
--- 2567,2582 ----
  }

  static void
+ _outTableSampleClause(StringInfo str, const TableSampleClause *node)
+ {
+     WRITE_NODE_TYPE("TABLESAMPLECLAUSE");
+
+     WRITE_OID_FIELD(tsmhandler);
+     WRITE_NODE_FIELD(args);
+     WRITE_NODE_FIELD(repeatable);
+ }
+
+ static void
  _outAExpr(StringInfo str, const A_Expr *node)
  {
      WRITE_NODE_TYPE("AEXPR");
*************** _outRangeFunction(StringInfo str, const
*** 2846,2851 ****
--- 2828,2845 ----
  }

  static void
+ _outRangeTableSample(StringInfo str, const RangeTableSample *node)
+ {
+     WRITE_NODE_TYPE("RANGETABLESAMPLE");
+
+     WRITE_NODE_FIELD(relation);
+     WRITE_NODE_FIELD(method);
+     WRITE_NODE_FIELD(args);
+     WRITE_NODE_FIELD(repeatable);
+     WRITE_LOCATION_FIELD(location);
+ }
+
+ static void
  _outConstraint(StringInfo str, const Constraint *node)
  {
      WRITE_NODE_TYPE("CONSTRAINT");
*************** _outNode(StringInfo str, const void *obj
*** 3002,3007 ****
--- 2996,3004 ----
              case T_SeqScan:
                  _outSeqScan(str, obj);
                  break;
+             case T_SampleScan:
+                 _outSampleScan(str, obj);
+                 break;
              case T_IndexScan:
                  _outIndexScan(str, obj);
                  break;
*************** _outNode(StringInfo str, const void *obj
*** 3038,3046 ****
              case T_CustomScan:
                  _outCustomScan(str, obj);
                  break;
-             case T_SampleScan:
-                 _outSampleScan(str, obj);
-                 break;
              case T_Join:
                  _outJoin(str, obj);
                  break;
--- 3035,3040 ----
*************** _outNode(StringInfo str, const void *obj
*** 3393,3404 ****
              case T_CommonTableExpr:
                  _outCommonTableExpr(str, obj);
                  break;
-             case T_RangeTableSample:
-                 _outRangeTableSample(str, obj);
-                 break;
-             case T_TableSampleClause:
-                 _outTableSampleClause(str, obj);
-                 break;
              case T_SetOperationStmt:
                  _outSetOperationStmt(str, obj);
                  break;
--- 3387,3392 ----
*************** _outNode(StringInfo str, const void *obj
*** 3408,3413 ****
--- 3396,3404 ----
              case T_RangeTblFunction:
                  _outRangeTblFunction(str, obj);
                  break;
+             case T_TableSampleClause:
+                 _outTableSampleClause(str, obj);
+                 break;
              case T_A_Expr:
                  _outAExpr(str, obj);
                  break;
*************** _outNode(StringInfo str, const void *obj
*** 3450,3455 ****
--- 3441,3449 ----
              case T_RangeFunction:
                  _outRangeFunction(str, obj);
                  break;
+             case T_RangeTableSample:
+                 _outRangeTableSample(str, obj);
+                 break;
              case T_Constraint:
                  _outConstraint(str, obj);
                  break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f5a40fb..71be840 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readCommonTableExpr(void)
*** 368,413 ****
  }

  /*
-  * _readRangeTableSample
-  */
- static RangeTableSample *
- _readRangeTableSample(void)
- {
-     READ_LOCALS(RangeTableSample);
-
-     READ_NODE_FIELD(relation);
-     READ_STRING_FIELD(method);
-     READ_NODE_FIELD(repeatable);
-     READ_NODE_FIELD(args);
-
-     READ_DONE();
- }
-
- /*
-  * _readTableSampleClause
-  */
- static TableSampleClause *
- _readTableSampleClause(void)
- {
-     READ_LOCALS(TableSampleClause);
-
-     READ_OID_FIELD(tsmid);
-     READ_BOOL_FIELD(tsmseqscan);
-     READ_BOOL_FIELD(tsmpagemode);
-     READ_OID_FIELD(tsminit);
-     READ_OID_FIELD(tsmnextblock);
-     READ_OID_FIELD(tsmnexttuple);
-     READ_OID_FIELD(tsmexaminetuple);
-     READ_OID_FIELD(tsmend);
-     READ_OID_FIELD(tsmreset);
-     READ_OID_FIELD(tsmcost);
-     READ_NODE_FIELD(repeatable);
-     READ_NODE_FIELD(args);
-
-     READ_DONE();
- }
-
- /*
   * _readSetOperationStmt
   */
  static SetOperationStmt *
--- 368,373 ----
*************** _readRangeTblFunction(void)
*** 1391,1396 ****
--- 1351,1371 ----
      READ_DONE();
  }

+ /*
+  * _readTableSampleClause
+  */
+ static TableSampleClause *
+ _readTableSampleClause(void)
+ {
+     READ_LOCALS(TableSampleClause);
+
+     READ_OID_FIELD(tsmhandler);
+     READ_NODE_FIELD(args);
+     READ_NODE_FIELD(repeatable);
+
+     READ_DONE();
+ }
+

  /*
   * parseNodeString
*************** parseNodeString(void)
*** 1426,1435 ****
          return_value = _readRowMarkClause();
      else if (MATCH("COMMONTABLEEXPR", 15))
          return_value = _readCommonTableExpr();
-     else if (MATCH("RANGETABLESAMPLE", 16))
-         return_value = _readRangeTableSample();
-     else if (MATCH("TABLESAMPLECLAUSE", 17))
-         return_value = _readTableSampleClause();
      else if (MATCH("SETOPERATIONSTMT", 16))
          return_value = _readSetOperationStmt();
      else if (MATCH("ALIAS", 5))
--- 1401,1406 ----
*************** parseNodeString(void)
*** 1528,1533 ****
--- 1499,1506 ----
          return_value = _readRangeTblEntry();
      else if (MATCH("RANGETBLFUNCTION", 16))
          return_value = _readRangeTblFunction();
+     else if (MATCH("TABLESAMPLECLAUSE", 17))
+         return_value = _readTableSampleClause();
      else if (MATCH("NOTIFY", 6))
          return_value = _readNotifyStmt();
      else if (MATCH("DECLARECURSOR", 13))
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0b83189..187db96 100644
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
***************
*** 18,23 ****
--- 18,24 ----
  #include <math.h>

  #include "access/sysattr.h"
+ #include "access/tsmapi.h"
  #include "catalog/pg_class.h"
  #include "catalog/pg_operator.h"
  #include "foreign/fdwapi.h"
*************** set_rel_pathlist(PlannerInfo *root, RelO
*** 390,396 ****
                  }
                  else if (rte->tablesample != NULL)
                  {
!                     /* Build sample scan on relation */
                      set_tablesample_rel_pathlist(root, rel, rte);
                  }
                  else
--- 391,397 ----
                  }
                  else if (rte->tablesample != NULL)
                  {
!                     /* Sampled relation */
                      set_tablesample_rel_pathlist(root, rel, rte);
                  }
                  else
*************** set_plain_rel_pathlist(PlannerInfo *root
*** 480,490 ****

  /*
   * set_tablesample_rel_size
!  *      Set size estimates for a sampled relation.
   */
  static void
  set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
      /* Mark rel with estimated output rows, width, etc */
      set_baserel_size_estimates(root, rel);
  }
--- 481,540 ----

  /*
   * set_tablesample_rel_size
!  *      Set size estimates for a sampled relation
   */
  static void
  set_tablesample_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
+     TableSampleClause *tsc = rte->tablesample;
+     TsmRoutine *tsm;
+     BlockNumber pages;
+     double        tuples;
+
+     /*
+      * Test any partial indexes of rel for applicability.  We must do this
+      * first since partial unique indexes can affect size estimates.
+      */
+     check_partial_indexes(root, rel);
+
+     /*
+      * Call the sampling method's estimation function to estimate the number
+      * of pages it will read and the number of tuples it will return.  (Note:
+      * we assume the function returns sane values.)
+      */
+     tsm = GetTsmRoutine(tsc->tsmhandler);
+     tsm->SampleScanCost(root, rel, tsc->args,
+                         &pages, &tuples);
+
+     /*
+      * If the sampling method does not support repeatable scans, we cannot
+      * safely put it on the inside of a nestloop join.  For the moment, just
+      * refuse to join at all.  Eventually it might be worth adding sufficient
+      * complication to the planner to handle such cases more fully.  Note that
+      * this test does not reject inheritance trees, only actual joins.
+      *
+      * Note: this test is not really sufficient, because we might be inside a
+      * subquery that will end up being scanned multiple times in the parent's
+      * plan.  But since sampling methods with this property are pretty much
+      * rubbish anyway, it doesn't seem worth working terribly hard to prevent
+      * inconsistent results.
+      */
+     if (!tsm->repeatable_across_scans &&
+         bms_membership(root->all_baserels) != BMS_SINGLETON)
+         ereport(ERROR,
+                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                  errmsg("tablesample method %s does not support joins",
+                         get_func_name(tsc->tsmhandler))));
+
+     /*
+      * For the moment, because we will only consider a SampleScan path for the
+      * rel, it's okay to just overwrite the pages and tuples estimates for the
+      * whole relation.  If we ever consider multiple path types for sampled
+      * rels, we'll need more complication.
+      */
+     rel->pages = pages;
+     rel->tuples = tuples;
+
      /* Mark rel with estimated output rows, width, etc */
      set_baserel_size_estimates(root, rel);
  }
*************** set_tablesample_rel_size(PlannerInfo *ro
*** 492,516 ****
  /*
   * set_tablesample_rel_pathlist
   *      Build access paths for a sampled relation
-  *
-  * There is only one possible path - sampling scan
   */
  static void
  set_tablesample_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
      Relids        required_outer;
-     Path       *path;

      /*
!      * We don't support pushing join clauses into the quals of a seqscan, but
!      * it could still have required parameterization due to LATERAL refs in
!      * its tlist.
       */
      required_outer = rel->lateral_relids;

!     /* We only do sample scan if it was requested */
!     path = create_samplescan_path(root, rel, required_outer);
!     rel->pathlist = list_make1(path);
  }

  /*
--- 542,564 ----
  /*
   * set_tablesample_rel_pathlist
   *      Build access paths for a sampled relation
   */
  static void
  set_tablesample_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
      Relids        required_outer;

      /*
!      * We don't support pushing join clauses into the quals of a samplescan,
!      * but it could still have required parameterization due to LATERAL refs
!      * in its tlist.
       */
      required_outer = rel->lateral_relids;

!     /* Consider sampled scan */
!     add_path(rel, create_samplescan_path(root, rel, required_outer));
!
!     /* For the moment, at least, there are no other paths to consider */
  }

  /*
*************** print_path(PlannerInfo *root, Path *path
*** 2410,2416 ****
      switch (nodeTag(path))
      {
          case T_Path:
!             ptype = "SeqScan";
              break;
          case T_IndexPath:
              ptype = "IdxScan";
--- 2458,2490 ----
      switch (nodeTag(path))
      {
          case T_Path:
!             switch (path->pathtype)
!             {
!                 case T_SeqScan:
!                     ptype = "SeqScan";
!                     break;
!                 case T_SampleScan:
!                     ptype = "SampleScan";
!                     break;
!                 case T_SubqueryScan:
!                     ptype = "SubqueryScan";
!                     break;
!                 case T_FunctionScan:
!                     ptype = "FunctionScan";
!                     break;
!                 case T_ValuesScan:
!                     ptype = "ValuesScan";
!                     break;
!                 case T_CteScan:
!                     ptype = "CteScan";
!                     break;
!                 case T_WorkTableScan:
!                     ptype = "WorkTableScan";
!                     break;
!                 default:
!                     ptype = "???Path";
!                     break;
!             }
              break;
          case T_IndexPath:
              ptype = "IdxScan";
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 0d302f6..0de9485 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
***************
*** 74,79 ****
--- 74,80 ----
  #include <math.h>

  #include "access/htup_details.h"
+ #include "access/tsmapi.h"
  #include "executor/executor.h"
  #include "executor/nodeHash.h"
  #include "miscadmin.h"
*************** cost_seqscan(Path *path, PlannerInfo *ro
*** 223,286 ****
   * cost_samplescan
   *      Determines and returns the cost of scanning a relation using sampling.
   *
-  * From planner/optimizer perspective, we don't care all that much about cost
-  * itself since there is always only one scan path to consider when sampling
-  * scan is present, but number of rows estimation is still important.
-  *
   * 'baserel' is the relation to be scanned
   * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
   */
  void
! cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
  {
      Cost        startup_cost = 0;
      Cost        run_cost = 0;
      double        spc_seq_page_cost,
                  spc_random_page_cost,
                  spc_page_cost;
      QualCost    qpqual_cost;
      Cost        cpu_per_tuple;
-     BlockNumber pages;
-     double        tuples;
-     RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
-     TableSampleClause *tablesample = rte->tablesample;

!     /* Should only be applied to base relations */
      Assert(baserel->relid > 0);
!     Assert(baserel->rtekind == RTE_RELATION);

      /* Mark the path with the correct row estimate */
!     if (path->param_info)
!         path->rows = path->param_info->ppi_rows;
      else
          path->rows = baserel->rows;

-     /* Call the sampling method's costing function. */
-     OidFunctionCall6(tablesample->tsmcost, PointerGetDatum(root),
-                      PointerGetDatum(path), PointerGetDatum(baserel),
-                      PointerGetDatum(tablesample->args),
-                      PointerGetDatum(&pages), PointerGetDatum(&tuples));
-
      /* fetch estimated page cost for tablespace containing table */
      get_tablespace_page_costs(baserel->reltablespace,
                                &spc_random_page_cost,
                                &spc_seq_page_cost);

!
!     spc_page_cost = tablesample->tsmseqscan ? spc_seq_page_cost :
!         spc_random_page_cost;

      /*
!      * disk costs
       */
!     run_cost += spc_page_cost * pages;

!     /* CPU costs */
!     get_restriction_qual_cost(root, baserel, path->param_info, &qpqual_cost);

      startup_cost += qpqual_cost.startup;
      cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
!     run_cost += cpu_per_tuple * tuples;

      path->startup_cost = startup_cost;
      path->total_cost = startup_cost + run_cost;
--- 224,288 ----
   * cost_samplescan
   *      Determines and returns the cost of scanning a relation using sampling.
   *
   * 'baserel' is the relation to be scanned
   * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
   */
  void
! cost_samplescan(Path *path, PlannerInfo *root,
!                 RelOptInfo *baserel, ParamPathInfo *param_info)
  {
      Cost        startup_cost = 0;
      Cost        run_cost = 0;
+     RangeTblEntry *rte;
+     TableSampleClause *tsc;
+     TsmRoutine *tsm;
      double        spc_seq_page_cost,
                  spc_random_page_cost,
                  spc_page_cost;
      QualCost    qpqual_cost;
      Cost        cpu_per_tuple;

!     /* Should only be applied to base relations with tablesample clauses */
      Assert(baserel->relid > 0);
!     rte = planner_rt_fetch(baserel->relid, root);
!     Assert(rte->rtekind == RTE_RELATION);
!     tsc = rte->tablesample;
!     Assert(tsc != NULL);
!     tsm = GetTsmRoutine(tsc->tsmhandler);

      /* Mark the path with the correct row estimate */
!     if (param_info)
!         path->rows = param_info->ppi_rows;
      else
          path->rows = baserel->rows;

      /* fetch estimated page cost for tablespace containing table */
      get_tablespace_page_costs(baserel->reltablespace,
                                &spc_random_page_cost,
                                &spc_seq_page_cost);

!     /* if NextSampleBlock is used, assume random access, else sequential */
!     spc_page_cost = (tsm->NextSampleBlock != NULL) ?
!         spc_random_page_cost : spc_seq_page_cost;

      /*
!      * disk costs (recall that baserel->pages has already been set to the
!      * number of pages the sampling method will visit)
       */
!     run_cost += spc_page_cost * baserel->pages;

!     /*
!      * CPU costs (recall that baserel->tuples has already been set to the
!      * number of tuples the sampling method will select).  Note that we ignore
!      * execution cost of the TABLESAMPLE parameter expressions; they will be
!      * evaluated only once per scan, and in most usages they'll likely be
!      * simple constants anyway.
!      */
!     get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);

      startup_cost += qpqual_cost.startup;
      cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
!     run_cost += cpu_per_tuple * baserel->tuples;

      path->startup_cost = startup_cost;
      path->total_cost = startup_cost + run_cost;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 8d15c8e..f461586 100644
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
*************** static List *order_qual_clauses(PlannerI
*** 102,108 ****
  static void copy_path_costsize(Plan *dest, Path *src);
  static void copy_plan_costsize(Plan *dest, Plan *src);
  static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
! static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid);
  static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
                 Oid indexid, List *indexqual, List *indexqualorig,
                 List *indexorderby, List *indexorderbyorig,
--- 102,109 ----
  static void copy_path_costsize(Plan *dest, Path *src);
  static void copy_plan_costsize(Plan *dest, Plan *src);
  static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
! static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
!                 TableSampleClause *tsc);
  static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
                 Oid indexid, List *indexqual, List *indexqualorig,
                 List *indexorderby, List *indexorderbyorig,
*************** create_seqscan_plan(PlannerInfo *root, P
*** 1148,1154 ****

  /*
   * create_samplescan_plan
!  *     Returns a samplecan plan for the base relation scanned by 'best_path'
   *     with restriction clauses 'scan_clauses' and targetlist 'tlist'.
   */
  static SampleScan *
--- 1149,1155 ----

  /*
   * create_samplescan_plan
!  *     Returns a samplescan plan for the base relation scanned by 'best_path'
   *     with restriction clauses 'scan_clauses' and targetlist 'tlist'.
   */
  static SampleScan *
*************** create_samplescan_plan(PlannerInfo *root
*** 1157,1167 ****
  {
      SampleScan *scan_plan;
      Index        scan_relid = best_path->parent->relid;

!     /* it should be a base rel with tablesample clause... */
      Assert(scan_relid > 0);
!     Assert(best_path->parent->rtekind == RTE_RELATION);
!     Assert(best_path->pathtype == T_SampleScan);

      /* Sort clauses into best execution order */
      scan_clauses = order_qual_clauses(root, scan_clauses);
--- 1158,1172 ----
  {
      SampleScan *scan_plan;
      Index        scan_relid = best_path->parent->relid;
+     RangeTblEntry *rte;
+     TableSampleClause *tsc;

!     /* it should be a base rel with a tablesample clause... */
      Assert(scan_relid > 0);
!     rte = planner_rt_fetch(scan_relid, root);
!     Assert(rte->rtekind == RTE_RELATION);
!     tsc = rte->tablesample;
!     Assert(tsc != NULL);

      /* Sort clauses into best execution order */
      scan_clauses = order_qual_clauses(root, scan_clauses);
*************** create_samplescan_plan(PlannerInfo *root
*** 1174,1186 ****
      {
          scan_clauses = (List *)
              replace_nestloop_params(root, (Node *) scan_clauses);
      }

      scan_plan = make_samplescan(tlist,
                                  scan_clauses,
!                                 scan_relid);

!     copy_path_costsize(&scan_plan->plan, best_path);

      return scan_plan;
  }
--- 1179,1194 ----
      {
          scan_clauses = (List *)
              replace_nestloop_params(root, (Node *) scan_clauses);
+         tsc = (TableSampleClause *)
+             replace_nestloop_params(root, (Node *) tsc);
      }

      scan_plan = make_samplescan(tlist,
                                  scan_clauses,
!                                 scan_relid,
!                                 tsc);

!     copy_path_costsize(&scan_plan->scan.plan, best_path);

      return scan_plan;
  }
*************** make_seqscan(List *qptlist,
*** 3437,3453 ****
  static SampleScan *
  make_samplescan(List *qptlist,
                  List *qpqual,
!                 Index scanrelid)
  {
      SampleScan *node = makeNode(SampleScan);
!     Plan       *plan = &node->plan;

      /* cost should be inserted by caller */
      plan->targetlist = qptlist;
      plan->qual = qpqual;
      plan->lefttree = NULL;
      plan->righttree = NULL;
!     node->scanrelid = scanrelid;

      return node;
  }
--- 3445,3463 ----
  static SampleScan *
  make_samplescan(List *qptlist,
                  List *qpqual,
!                 Index scanrelid,
!                 TableSampleClause *tsc)
  {
      SampleScan *node = makeNode(SampleScan);
!     Plan       *plan = &node->scan.plan;

      /* cost should be inserted by caller */
      plan->targetlist = qptlist;
      plan->qual = qpqual;
      plan->lefttree = NULL;
      plan->righttree = NULL;
!     node->scan.scanrelid = scanrelid;
!     node->tablesample = tsc;

      return node;
  }
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 00b2625..701b992 100644
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
*************** extract_lateral_references(PlannerInfo *
*** 306,312 ****
          return;

      /* Fetch the appropriate variables */
!     if (rte->rtekind == RTE_SUBQUERY)
          vars = pull_vars_of_level((Node *) rte->subquery, 1);
      else if (rte->rtekind == RTE_FUNCTION)
          vars = pull_vars_of_level((Node *) rte->functions, 0);
--- 306,314 ----
          return;

      /* Fetch the appropriate variables */
!     if (rte->rtekind == RTE_RELATION)
!         vars = pull_vars_of_level((Node *) rte->tablesample, 0);
!     else if (rte->rtekind == RTE_SUBQUERY)
          vars = pull_vars_of_level((Node *) rte->subquery, 1);
      else if (rte->rtekind == RTE_FUNCTION)
          vars = pull_vars_of_level((Node *) rte->functions, 0);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a6ce96e..b95cc95 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** subquery_planner(PlannerGlobal *glob, Qu
*** 505,518 ****
          if (rte->rtekind == RTE_RELATION)
          {
              if (rte->tablesample)
!             {
!                 rte->tablesample->args = (List *)
!                     preprocess_expression(root, (Node *) rte->tablesample->args,
!                                           EXPRKIND_TABLESAMPLE);
!                 rte->tablesample->repeatable = (Node *)
!                     preprocess_expression(root, rte->tablesample->repeatable,
                                            EXPRKIND_TABLESAMPLE);
-             }
          }
          else if (rte->rtekind == RTE_SUBQUERY)
          {
--- 505,514 ----
          if (rte->rtekind == RTE_RELATION)
          {
              if (rte->tablesample)
!                 rte->tablesample = (TableSampleClause *)
!                     preprocess_expression(root,
!                                           (Node *) rte->tablesample,
                                            EXPRKIND_TABLESAMPLE);
          }
          else if (rte->rtekind == RTE_SUBQUERY)
          {
*************** preprocess_expression(PlannerInfo *root,
*** 697,707 ****
       * If the query has any join RTEs, replace join alias variables with
       * base-relation variables.  We must do this before sublink processing,
       * else sublinks expanded out from join aliases would not get processed.
!      * We can skip it in non-lateral RTE functions and VALUES lists, however,
!      * since they can't contain any Vars of the current query level.
       */
      if (root->hasJoinRTEs &&
!         !(kind == EXPRKIND_RTFUNC || kind == EXPRKIND_VALUES))
          expr = flatten_join_alias_vars(root, expr);

      /*
--- 693,706 ----
       * If the query has any join RTEs, replace join alias variables with
       * base-relation variables.  We must do this before sublink processing,
       * else sublinks expanded out from join aliases would not get processed.
!      * We can skip it in non-lateral RTE functions, VALUES lists, and
!      * TABLESAMPLE clauses, however, since they can't contain any Vars of the
!      * current query level.
       */
      if (root->hasJoinRTEs &&
!         !(kind == EXPRKIND_RTFUNC ||
!           kind == EXPRKIND_VALUES ||
!           kind == EXPRKIND_TABLESAMPLE))
          expr = flatten_join_alias_vars(root, expr);

      /*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 258e541..ea185d4 100644
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
*************** flatten_rtes_walker(Node *node, PlannerG
*** 372,380 ****
   *
   * In the flat rangetable, we zero out substructure pointers that are not
   * needed by the executor; this reduces the storage space and copying cost
!  * for cached plans.  We keep only the tablesample field (which we'd otherwise
!  * have to put in the plan tree, anyway); the ctename, alias and eref Alias
!  * fields, which are needed by EXPLAIN; and the selectedCols, insertedCols and
   * updatedCols bitmaps, which are needed for executor-startup permissions
   * checking and for trigger event checking.
   */
--- 372,379 ----
   *
   * In the flat rangetable, we zero out substructure pointers that are not
   * needed by the executor; this reduces the storage space and copying cost
!  * for cached plans.  We keep only the ctename, alias and eref Alias fields,
!  * which are needed by EXPLAIN, and the selectedCols, insertedCols and
   * updatedCols bitmaps, which are needed for executor-startup permissions
   * checking and for trigger event checking.
   */
*************** add_rte_to_flat_rtable(PlannerGlobal *gl
*** 388,393 ****
--- 387,393 ----
      memcpy(newrte, rte, sizeof(RangeTblEntry));

      /* zap unneeded sub-structure */
+     newrte->tablesample = NULL;
      newrte->subquery = NULL;
      newrte->joinaliasvars = NIL;
      newrte->functions = NIL;
*************** set_plan_refs(PlannerInfo *root, Plan *p
*** 456,466 ****
              {
                  SampleScan *splan = (SampleScan *) plan;

!                 splan->scanrelid += rtoffset;
!                 splan->plan.targetlist =
!                     fix_scan_list(root, splan->plan.targetlist, rtoffset);
!                 splan->plan.qual =
!                     fix_scan_list(root, splan->plan.qual, rtoffset);
              }
              break;
          case T_IndexScan:
--- 456,468 ----
              {
                  SampleScan *splan = (SampleScan *) plan;

!                 splan->scan.scanrelid += rtoffset;
!                 splan->scan.plan.targetlist =
!                     fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
!                 splan->scan.plan.qual =
!                     fix_scan_list(root, splan->scan.plan.qual, rtoffset);
!                 splan->tablesample = (TableSampleClause *)
!                     fix_scan_expr(root, (Node *) splan->tablesample, rtoffset);
              }
              break;
          case T_IndexScan:
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 4708b87..f3038cd 100644
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2216,2222 ****
--- 2216,2227 ----
              break;

          case T_SeqScan:
+             context.paramids = bms_add_members(context.paramids, scan_params);
+             break;
+
          case T_SampleScan:
+             finalize_primnode((Node *) ((SampleScan *) plan)->tablesample,
+                               &context);
              context.paramids = bms_add_members(context.paramids, scan_params);
              break;

diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 92b0562..34144cc 100644
*** a/src/backend/optimizer/prep/prepjointree.c
--- b/src/backend/optimizer/prep/prepjointree.c
*************** pull_up_simple_subquery(PlannerInfo *roo
*** 1091,1102 ****

              switch (child_rte->rtekind)
              {
                  case RTE_SUBQUERY:
                  case RTE_FUNCTION:
                  case RTE_VALUES:
                      child_rte->lateral = true;
                      break;
-                 case RTE_RELATION:
                  case RTE_JOIN:
                  case RTE_CTE:
                      /* these can't contain any lateral references */
--- 1091,1105 ----

              switch (child_rte->rtekind)
              {
+                 case RTE_RELATION:
+                     if (child_rte->tablesample)
+                         child_rte->lateral = true;
+                     break;
                  case RTE_SUBQUERY:
                  case RTE_FUNCTION:
                  case RTE_VALUES:
                      child_rte->lateral = true;
                      break;
                  case RTE_JOIN:
                  case RTE_CTE:
                      /* these can't contain any lateral references */
*************** replace_vars_in_jointree(Node *jtnode,
*** 1909,1914 ****
--- 1912,1924 ----
              {
                  switch (rte->rtekind)
                  {
+                     case RTE_RELATION:
+                         /* shouldn't be marked LATERAL unless tablesample */
+                         Assert(rte->tablesample);
+                         rte->tablesample = (TableSampleClause *)
+                             pullup_replace_vars((Node *) rte->tablesample,
+                                                 context);
+                         break;
                      case RTE_SUBQUERY:
                          rte->subquery =
                              pullup_replace_vars_subquery(rte->subquery,
*************** replace_vars_in_jointree(Node *jtnode,
*** 1924,1930 ****
                              pullup_replace_vars((Node *) rte->values_lists,
                                                  context);
                          break;
-                     case RTE_RELATION:
                      case RTE_JOIN:
                      case RTE_CTE:
                          /* these shouldn't be marked LATERAL */
--- 1934,1939 ----
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index f7f33bb..935bc2b 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** create_seqscan_path(PlannerInfo *root, R
*** 713,719 ****

  /*
   * create_samplescan_path
!  *      Like seqscan but uses sampling function while scanning.
   */
  Path *
  create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
--- 713,719 ----

  /*
   * create_samplescan_path
!  *      Creates a path node for a sampled table scan.
   */
  Path *
  create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
*************** create_samplescan_path(PlannerInfo *root
*** 726,732 ****
                                                       required_outer);
      pathnode->pathkeys = NIL;    /* samplescan has unordered result */

!     cost_samplescan(pathnode, root, rel);

      return pathnode;
  }
--- 726,732 ----
                                                       required_outer);
      pathnode->pathkeys = NIL;    /* samplescan has unordered result */

!     cost_samplescan(pathnode, root, rel, pathnode->param_info);

      return pathnode;
  }
*************** reparameterize_path(PlannerInfo *root, P
*** 1773,1778 ****
--- 1773,1780 ----
      {
          case T_SeqScan:
              return create_seqscan_path(root, rel, required_outer);
+         case T_SampleScan:
+             return (Path *) create_samplescan_path(root, rel, required_outer);
          case T_IndexScan:
          case T_IndexOnlyScan:
              {
*************** reparameterize_path(PlannerInfo *root, P
*** 1805,1812 ****
          case T_SubqueryScan:
              return create_subqueryscan_path(root, rel, path->pathkeys,
                                              required_outer);
-         case T_SampleScan:
-             return (Path *) create_samplescan_path(root, rel, required_outer);
          default:
              break;
      }
--- 1807,1812 ----
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2b02a2e..8f053e4 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 457,464 ****
  %type <jexpr>    joined_table
  %type <range>    relation_expr
  %type <range>    relation_expr_opt_alias
  %type <target>    target_el single_set_clause set_target insert_column_item
- %type <node>    relation_expr_tablesample tablesample_clause opt_repeatable_clause

  %type <str>        generic_option_name
  %type <node>    generic_option_arg
--- 457,464 ----
  %type <jexpr>    joined_table
  %type <range>    relation_expr
  %type <range>    relation_expr_opt_alias
+ %type <node>    tablesample_clause opt_repeatable_clause
  %type <target>    target_el single_set_clause set_target insert_column_item

  %type <str>        generic_option_name
  %type <node>    generic_option_arg
*************** table_ref:    relation_expr opt_alias_claus
*** 10491,10499 ****
                      $1->alias = $2;
                      $$ = (Node *) $1;
                  }
!             | relation_expr_tablesample
                  {
!                     $$ = (Node *) $1;
                  }
              | func_table func_alias_clause
                  {
--- 10491,10503 ----
                      $1->alias = $2;
                      $$ = (Node *) $1;
                  }
!             | relation_expr opt_alias_clause tablesample_clause
                  {
!                     RangeTableSample *n = (RangeTableSample *) $3;
!                     $1->alias = $2;
!                     /* relation_expr goes inside the RangeTableSample node */
!                     n->relation = (Node *) $1;
!                     $$ = (Node *) n;
                  }
              | func_table func_alias_clause
                  {
*************** relation_expr_opt_alias: relation_expr
*** 10820,10842 ****
                  }
          ;

!
! relation_expr_tablesample: relation_expr opt_alias_clause tablesample_clause
!                 {
!                     RangeTableSample *n = (RangeTableSample *) $3;
!                     n->relation = $1;
!                     n->relation->alias = $2;
!                     $$ = (Node *) n;
!                 }
!         ;
!
  tablesample_clause:
!             TABLESAMPLE ColId '(' expr_list ')' opt_repeatable_clause
                  {
                      RangeTableSample *n = makeNode(RangeTableSample);
                      n->method = $2;
                      n->args = $4;
                      n->repeatable = $6;
                      $$ = (Node *) n;
                  }
          ;
--- 10824,10841 ----
                  }
          ;

! /*
!  * TABLESAMPLE decoration in a FROM item
!  */
  tablesample_clause:
!             TABLESAMPLE func_name '(' expr_list ')' opt_repeatable_clause
                  {
                      RangeTableSample *n = makeNode(RangeTableSample);
+                     /* n->relation will be filled in later */
                      n->method = $2;
                      n->args = $4;
                      n->repeatable = $6;
+                     n->location = @2;
                      $$ = (Node *) n;
                  }
          ;
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index e90e1d6..4e490b2 100644
*** a/src/backend/parser/parse_clause.c
--- b/src/backend/parser/parse_clause.c
***************
*** 18,25 ****
  #include "miscadmin.h"

  #include "access/heapam.h"
  #include "catalog/catalog.h"
- #include "access/htup_details.h"
  #include "catalog/heap.h"
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_type.h"
--- 18,25 ----
  #include "miscadmin.h"

  #include "access/heapam.h"
+ #include "access/tsmapi.h"
  #include "catalog/catalog.h"
  #include "catalog/heap.h"
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_type.h"
***************
*** 43,49 ****
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
! #include "utils/syscache.h"

  /* Convenience macro for the most common makeNamespaceItem() case */
  #define makeDefaultNSItem(rte)    makeNamespaceItem(rte, true, true, false, true)
--- 43,49 ----
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
!

  /* Convenience macro for the most common makeNamespaceItem() case */
  #define makeDefaultNSItem(rte)    makeNamespaceItem(rte, true, true, false, true)
*************** static RangeTblEntry *transformRangeSubs
*** 63,68 ****
--- 63,70 ----
                          RangeSubselect *r);
  static RangeTblEntry *transformRangeFunction(ParseState *pstate,
                         RangeFunction *r);
+ static TableSampleClause *transformRangeTableSample(ParseState *pstate,
+                           RangeTableSample *rts);
  static Node *transformFromClauseItem(ParseState *pstate, Node *n,
                          RangeTblEntry **top_rte, int *top_rti,
                          List **namespace);
*************** transformJoinOnClause(ParseState *pstate
*** 423,462 ****
      return result;
  }

- static RangeTblEntry *
- transformTableSampleEntry(ParseState *pstate, RangeTableSample *rv)
- {
-     RangeTblEntry *rte = NULL;
-     CommonTableExpr *cte = NULL;
-     TableSampleClause *tablesample = NULL;
-
-     /* if relation has an unqualified name, it might be a CTE reference */
-     if (!rv->relation->schemaname)
-     {
-         Index        levelsup;
-
-         cte = scanNameSpaceForCTE(pstate, rv->relation->relname, &levelsup);
-     }
-
-     /* We first need to build a range table entry */
-     if (!cte)
-         rte = transformTableEntry(pstate, rv->relation);
-
-     if (!rte ||
-         (rte->relkind != RELKIND_RELATION &&
-          rte->relkind != RELKIND_MATVIEW))
-         ereport(ERROR,
-                 (errcode(ERRCODE_SYNTAX_ERROR),
-                  errmsg("TABLESAMPLE clause can only be used on tables and materialized views"),
-                  parser_errposition(pstate, rv->relation->location)));
-
-     tablesample = ParseTableSample(pstate, rv->method, rv->repeatable,
-                                    rv->args, rv->relation->location);
-     rte->tablesample = tablesample;
-
-     return rte;
- }
-
  /*
   * transformTableEntry --- transform a RangeVar (simple relation reference)
   */
--- 425,430 ----
*************** transformRangeFunction(ParseState *pstat
*** 748,753 ****
--- 716,824 ----
      return rte;
  }

+ /*
+  * transformRangeTableSample --- transform a TABLESAMPLE clause
+  *
+  * Caller has already transformed rts->relation, we just have to validate
+  * the remaining fields and create a TableSampleClause node.
+  */
+ static TableSampleClause *
+ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
+ {
+     TableSampleClause *tablesample;
+     Oid            handlerOid;
+     Oid            funcargtypes[1];
+     TsmRoutine *tsm;
+     List       *fargs;
+     ListCell   *larg,
+                *ltyp;
+
+     /*
+      * To validate the sample method name, look up the handler function, which
+      * has the same name, one dummy INTERNAL argument, and a result type of
+      * tsm_handler.  (Note: tablesample method names are not schema-qualified
+      * in the SQL standard; but since they are just functions to us, we allow
+      * schema qualification to resolve any potential ambiguity.)
+      */
+     funcargtypes[0] = INTERNALOID;
+
+     handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true);
+
+     /* we want error to complain about no-such-method, not no-such-function */
+     if (!OidIsValid(handlerOid))
+         ereport(ERROR,
+                 (errcode(ERRCODE_UNDEFINED_OBJECT),
+                  errmsg("tablesample method %s does not exist",
+                         NameListToString(rts->method)),
+                  parser_errposition(pstate, rts->location)));
+
+     /* check that handler has correct return type */
+     if (get_func_rettype(handlerOid) != TSM_HANDLEROID)
+         ereport(ERROR,
+                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                  errmsg("function %s must return type \"tsm_handler\"",
+                         NameListToString(rts->method)),
+                  parser_errposition(pstate, rts->location)));
+
+     /* OK, run the handler to get TsmRoutine, for argument type info */
+     tsm = GetTsmRoutine(handlerOid);
+
+     tablesample = makeNode(TableSampleClause);
+     tablesample->tsmhandler = handlerOid;
+
+     /* check user provided the expected number of arguments */
+     if (list_length(rts->args) != list_length(tsm->parameterTypes))
+         ereport(ERROR,
+                 (errcode(ERRCODE_INVALID_TABLESAMPLE_ARGUMENT),
+           errmsg_plural("tablesample method %s requires %d argument, not %d",
+                         "tablesample method %s requires %d arguments, not %d",
+                         list_length(tsm->parameterTypes),
+                         NameListToString(rts->method),
+                         list_length(tsm->parameterTypes),
+                         list_length(rts->args)),
+                  parser_errposition(pstate, rts->location)));
+
+     /*
+      * Transform the arguments, typecasting them as needed.  Note we must also
+      * assign collations now, because assign_query_collations() doesn't
+      * examine any substructure of RTEs.
+      */
+     fargs = NIL;
+     forboth(larg, rts->args, ltyp, tsm->parameterTypes)
+     {
+         Node       *arg = (Node *) lfirst(larg);
+         Oid            argtype = lfirst_oid(ltyp);
+
+         arg = transformExpr(pstate, arg, EXPR_KIND_FROM_FUNCTION);
+         arg = coerce_to_specific_type(pstate, arg, argtype, "TABLESAMPLE");
+         assign_expr_collations(pstate, arg);
+         fargs = lappend(fargs, arg);
+     }
+     tablesample->args = fargs;
+
+     /* Process REPEATABLE (seed) */
+     if (rts->repeatable != NULL)
+     {
+         Node       *arg;
+
+         if (!tsm->repeatable_across_queries)
+             ereport(ERROR,
+                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                   errmsg("tablesample method %s does not support REPEATABLE",
+                          NameListToString(rts->method)),
+                      parser_errposition(pstate, rts->location)));
+
+         arg = transformExpr(pstate, rts->repeatable, EXPR_KIND_FROM_FUNCTION);
+         arg = coerce_to_specific_type(pstate, arg, FLOAT8OID, "REPEATABLE");
+         assign_expr_collations(pstate, arg);
+         tablesample->repeatable = (Expr *) arg;
+     }
+     else
+         tablesample->repeatable = NULL;
+
+     return tablesample;
+ }
+

  /*
   * transformFromClauseItem -
*************** transformFromClauseItem(ParseState *psta
*** 844,849 ****
--- 915,947 ----
          rtr->rtindex = rtindex;
          return (Node *) rtr;
      }
+     else if (IsA(n, RangeTableSample))
+     {
+         /* TABLESAMPLE clause (wrapping some other valid FROM node) */
+         RangeTableSample *rts = (RangeTableSample *) n;
+         Node       *rel;
+         RangeTblRef *rtr;
+         RangeTblEntry *rte;
+
+         /* Recursively transform the contained relation */
+         rel = transformFromClauseItem(pstate, rts->relation,
+                                       top_rte, top_rti, namespace);
+         /* Currently, grammar could only return a RangeVar as contained rel */
+         Assert(IsA(rel, RangeTblRef));
+         rtr = (RangeTblRef *) rel;
+         rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
+         /* We only support this on plain relations and matviews */
+         if (rte->relkind != RELKIND_RELATION &&
+             rte->relkind != RELKIND_MATVIEW)
+             ereport(ERROR,
+                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                      errmsg("TABLESAMPLE clause can only be applied to tables and materialized views"),
+                    parser_errposition(pstate, exprLocation(rts->relation))));
+
+         /* Transform TABLESAMPLE details and attach to the RTE */
+         rte->tablesample = transformRangeTableSample(pstate, rts);
+         return (Node *) rtr;
+     }
      else if (IsA(n, JoinExpr))
      {
          /* A newfangled join expression */
*************** transformFromClauseItem(ParseState *psta
*** 1165,1190 ****

          return (Node *) j;
      }
-     else if (IsA(n, RangeTableSample))
-     {
-         /* Tablesample reference */
-         RangeTableSample *rv = (RangeTableSample *) n;
-         RangeTblRef *rtr;
-         RangeTblEntry *rte = NULL;
-         int            rtindex;
-
-         rte = transformTableSampleEntry(pstate, rv);
-
-         /* assume new rte is at end */
-         rtindex = list_length(pstate->p_rtable);
-         Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
-         *top_rte = rte;
-         *top_rti = rtindex;
-         *namespace = list_make1(makeDefaultNSItem(rte));
-         rtr = makeNode(RangeTblRef);
-         rtr->rtindex = rtindex;
-         return (Node *) rtr;
-     }
      else
          elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));
      return NULL;                /* can't get here, keep compiler quiet */
--- 1263,1268 ----
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 430baff..554ca9d 100644
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
***************
*** 18,24 ****
  #include "catalog/pg_aggregate.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
- #include "catalog/pg_tablesample_method.h"
  #include "funcapi.h"
  #include "lib/stringinfo.h"
  #include "nodes/makefuncs.h"
--- 18,23 ----
***************
*** 27,33 ****
  #include "parser/parse_clause.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_func.h"
- #include "parser/parse_expr.h"
  #include "parser/parse_relation.h"
  #include "parser/parse_target.h"
  #include "parser/parse_type.h"
--- 26,31 ----
*************** ParseFuncOrColumn(ParseState *pstate, Li
*** 769,916 ****
  }


- /*
-  * ParseTableSample
-  *
-  * Parse TABLESAMPLE clause and process the arguments
-  */
- TableSampleClause *
- ParseTableSample(ParseState *pstate, char *samplemethod, Node *repeatable,
-                  List *sampleargs, int location)
- {
-     HeapTuple    tuple;
-     Form_pg_tablesample_method tsm;
-     Form_pg_proc procform;
-     TableSampleClause *tablesample;
-     List       *fargs;
-     ListCell   *larg;
-     int            nargs,
-                 initnargs;
-     Oid            init_arg_types[FUNC_MAX_ARGS];
-
-     /* Load the tablesample method */
-     tuple = SearchSysCache1(TABLESAMPLEMETHODNAME, PointerGetDatum(samplemethod));
-     if (!HeapTupleIsValid(tuple))
-         ereport(ERROR,
-                 (errcode(ERRCODE_UNDEFINED_OBJECT),
-                  errmsg("tablesample method \"%s\" does not exist",
-                         samplemethod),
-                  parser_errposition(pstate, location)));
-
-     tablesample = makeNode(TableSampleClause);
-     tablesample->tsmid = HeapTupleGetOid(tuple);
-
-     tsm = (Form_pg_tablesample_method) GETSTRUCT(tuple);
-
-     tablesample->tsmseqscan = tsm->tsmseqscan;
-     tablesample->tsmpagemode = tsm->tsmpagemode;
-     tablesample->tsminit = tsm->tsminit;
-     tablesample->tsmnextblock = tsm->tsmnextblock;
-     tablesample->tsmnexttuple = tsm->tsmnexttuple;
-     tablesample->tsmexaminetuple = tsm->tsmexaminetuple;
-     tablesample->tsmend = tsm->tsmend;
-     tablesample->tsmreset = tsm->tsmreset;
-     tablesample->tsmcost = tsm->tsmcost;
-
-     ReleaseSysCache(tuple);
-
-     /* Validate the parameters against init function definition. */
-     tuple = SearchSysCache1(PROCOID,
-                             ObjectIdGetDatum(tablesample->tsminit));
-
-     if (!HeapTupleIsValid(tuple))        /* should not happen */
-         elog(ERROR, "cache lookup failed for function %u",
-              tablesample->tsminit);
-
-     procform = (Form_pg_proc) GETSTRUCT(tuple);
-     initnargs = procform->pronargs;
-     Assert(initnargs >= 3);
-
-     /*
-      * First parameter is used to pass the SampleScanState, second is seed
-      * (REPEATABLE), skip the processing for them here, just assert that the
-      * types are correct.
-      */
-     Assert(procform->proargtypes.values[0] == INTERNALOID);
-     Assert(procform->proargtypes.values[1] == INT4OID);
-     initnargs -= 2;
-     memcpy(init_arg_types, procform->proargtypes.values + 2,
-            initnargs * sizeof(Oid));
-
-     /* Now we are done with the catalog */
-     ReleaseSysCache(tuple);
-
-     /* Process repeatable (seed) */
-     if (repeatable != NULL)
-     {
-         Node       *arg = repeatable;
-
-         if (arg && IsA(arg, A_Const))
-         {
-             A_Const    *con = (A_Const *) arg;
-
-             if (con->val.type == T_Null)
-                 ereport(ERROR,
-                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                   errmsg("REPEATABLE clause must be NOT NULL numeric value"),
-                          parser_errposition(pstate, con->location)));
-
-         }
-
-         arg = transformExpr(pstate, arg, EXPR_KIND_FROM_FUNCTION);
-         arg = coerce_to_specific_type(pstate, arg, INT4OID, "REPEATABLE");
-         tablesample->repeatable = arg;
-     }
-     else
-         tablesample->repeatable = NULL;
-
-     /* Check user provided expected number of arguments. */
-     if (list_length(sampleargs) != initnargs)
-         ereport(ERROR,
-                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-         errmsg_plural("tablesample method \"%s\" expects %d argument got %d",
-                       "tablesample method \"%s\" expects %d arguments got %d",
-                       initnargs,
-                       samplemethod,
-                       initnargs, list_length(sampleargs)),
-                  parser_errposition(pstate, location)));
-
-     /* Transform the arguments, typecasting them as needed. */
-     fargs = NIL;
-     nargs = 0;
-     foreach(larg, sampleargs)
-     {
-         Node       *inarg = (Node *) lfirst(larg);
-         Node       *arg = transformExpr(pstate, inarg, EXPR_KIND_FROM_FUNCTION);
-         Oid            argtype = exprType(arg);
-
-         if (argtype != init_arg_types[nargs])
-         {
-             if (!can_coerce_type(1, &argtype, &init_arg_types[nargs],
-                                  COERCION_IMPLICIT))
-                 ereport(ERROR,
-                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                    errmsg("wrong parameter %d for tablesample method \"%s\"",
-                           nargs + 1, samplemethod),
-                          errdetail("Expected type %s got %s.",
-                                    format_type_be(init_arg_types[nargs]),
-                                    format_type_be(argtype)),
-                          parser_errposition(pstate, exprLocation(inarg))));
-
-             arg = coerce_type(pstate, arg, argtype, init_arg_types[nargs], -1,
-                               COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
-         }
-
-         fargs = lappend(fargs, arg);
-         nargs++;
-     }
-
-     /* Pass the arguments down */
-     tablesample->args = fargs;
-
-     return tablesample;
- }
-
  /* func_match_argtypes()
   *
   * Given a list of candidate functions (having the right name and number
--- 767,772 ----
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index bbd6b77..1734e48 100644
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
*************** rewriteRuleAction(Query *parsetree,
*** 418,423 ****
--- 418,427 ----

              switch (rte->rtekind)
              {
+                 case RTE_RELATION:
+                     sub_action->hasSubLinks =
+                         checkExprHasSubLink((Node *) rte->tablesample);
+                     break;
                  case RTE_FUNCTION:
                      sub_action->hasSubLinks =
                          checkExprHasSubLink((Node *) rte->functions);
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 9ad460a..5b809aa 100644
*** a/src/backend/utils/adt/pseudotypes.c
--- b/src/backend/utils/adt/pseudotypes.c
*************** fdw_handler_out(PG_FUNCTION_ARGS)
*** 374,379 ****
--- 374,406 ----


  /*
+  * tsm_handler_in        - input routine for pseudo-type TSM_HANDLER.
+  */
+ Datum
+ tsm_handler_in(PG_FUNCTION_ARGS)
+ {
+     ereport(ERROR,
+             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+              errmsg("cannot accept a value of type tsm_handler")));
+
+     PG_RETURN_VOID();            /* keep compiler quiet */
+ }
+
+ /*
+  * tsm_handler_out        - output routine for pseudo-type TSM_HANDLER.
+  */
+ Datum
+ tsm_handler_out(PG_FUNCTION_ARGS)
+ {
+     ereport(ERROR,
+             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+              errmsg("cannot display a value of type tsm_handler")));
+
+     PG_RETURN_VOID();            /* keep compiler quiet */
+ }
+
+
+ /*
   * internal_in        - input routine for pseudo-type INTERNAL.
   */
  Datum
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5112cac..51391f6 100644
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 32,38 ****
  #include "catalog/pg_opclass.h"
  #include "catalog/pg_operator.h"
  #include "catalog/pg_proc.h"
- #include "catalog/pg_tablesample_method.h"
  #include "catalog/pg_trigger.h"
  #include "catalog/pg_type.h"
  #include "commands/defrem.h"
--- 32,37 ----
*************** static void make_ruledef(StringInfo buf,
*** 349,356 ****
               int prettyFlags);
  static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
               int prettyFlags, int wrapColumn);
- static void get_tablesample_def(TableSampleClause *tablesample,
-                     deparse_context *context);
  static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
                TupleDesc resultDesc,
                int prettyFlags, int wrapColumn, int startIndent);
--- 348,353 ----
*************** static void get_column_alias_list(depars
*** 416,421 ****
--- 413,420 ----
  static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
                             deparse_columns *colinfo,
                             deparse_context *context);
+ static void get_tablesample_def(TableSampleClause *tablesample,
+                     deparse_context *context);
  static void get_opclass_name(Oid opclass, Oid actual_datatype,
                   StringInfo buf);
  static Node *processIndirection(Node *node, deparse_context *context,
*************** make_viewdef(StringInfo buf, HeapTuple r
*** 4235,4284 ****
      heap_close(ev_relation, AccessShareLock);
  }

- /* ----------
-  * get_tablesample_def            - Convert TableSampleClause back to SQL
-  * ----------
-  */
- static void
- get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
- {
-     StringInfo    buf = context->buf;
-     HeapTuple    tuple;
-     Form_pg_tablesample_method tsm;
-     char       *tsmname;
-     int            nargs;
-     ListCell   *l;
-
-     /* Load the tablesample method */
-     tuple = SearchSysCache1(TABLESAMPLEMETHODOID, ObjectIdGetDatum(tablesample->tsmid));
-     if (!HeapTupleIsValid(tuple))
-         ereport(ERROR,
-                 (errcode(ERRCODE_UNDEFINED_OBJECT),
-                  errmsg("cache lookup failed for tablesample method %u",
-                         tablesample->tsmid)));
-
-     tsm = (Form_pg_tablesample_method) GETSTRUCT(tuple);
-     tsmname = NameStr(tsm->tsmname);
-     appendStringInfo(buf, " TABLESAMPLE %s (", quote_identifier(tsmname));
-
-     ReleaseSysCache(tuple);
-
-     nargs = 0;
-     foreach(l, tablesample->args)
-     {
-         if (nargs++ > 0)
-             appendStringInfoString(buf, ", ");
-         get_rule_expr((Node *) lfirst(l), context, true);
-     }
-     appendStringInfoChar(buf, ')');
-
-     if (tablesample->repeatable != NULL)
-     {
-         appendStringInfoString(buf, " REPEATABLE (");
-         get_rule_expr(tablesample->repeatable, context, true);
-         appendStringInfoChar(buf, ')');
-     }
- }

  /* ----------
   * get_query_def            - Parse back one query parsetree
--- 4234,4239 ----
*************** get_from_clause_item(Node *jtnode, Query
*** 8781,8789 ****
                                   only_marker(rte),
                                   generate_relation_name(rte->relid,
                                                          context->namespaces));
-
-                 if (rte->tablesample)
-                     get_tablesample_def(rte->tablesample, context);
                  break;
              case RTE_SUBQUERY:
                  /* Subquery RTE */
--- 8736,8741 ----
*************** get_from_clause_item(Node *jtnode, Query
*** 8963,8968 ****
--- 8915,8924 ----
              /* Else print column aliases as needed */
              get_column_alias_list(colinfo, context);
          }
+
+         /* Tablesample clause must go after any alias */
+         if (rte->rtekind == RTE_RELATION && rte->tablesample)
+             get_tablesample_def(rte->tablesample, context);
      }
      else if (IsA(jtnode, JoinExpr))
      {
*************** get_from_clause_coldeflist(RangeTblFunct
*** 9163,9168 ****
--- 9119,9162 ----
  }

  /*
+  * get_tablesample_def            - print a TableSampleClause
+  */
+ static void
+ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
+ {
+     StringInfo    buf = context->buf;
+     Oid            argtypes[1];
+     int            nargs;
+     ListCell   *l;
+
+     /*
+      * We should qualify the handler's function name if it wouldn't be
+      * resolved by lookup in the current search path.
+      */
+     argtypes[0] = INTERNALOID;
+     appendStringInfo(buf, " TABLESAMPLE %s (",
+                      generate_function_name(tablesample->tsmhandler, 1,
+                                             NIL, argtypes,
+                                             false, NULL, EXPR_KIND_NONE));
+
+     nargs = 0;
+     foreach(l, tablesample->args)
+     {
+         if (nargs++ > 0)
+             appendStringInfoString(buf, ", ");
+         get_rule_expr((Node *) lfirst(l), context, false);
+     }
+     appendStringInfoChar(buf, ')');
+
+     if (tablesample->repeatable != NULL)
+     {
+         appendStringInfoString(buf, " REPEATABLE (");
+         get_rule_expr((Node *) tablesample->repeatable, context, false);
+         appendStringInfoChar(buf, ')');
+     }
+ }
+
+ /*
   * get_opclass_name            - fetch name of an index operator class
   *
   * The opclass name is appended (after a space) to buf.
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 7b32247..1dc2932 100644
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
***************
*** 32,38 ****
  #include "catalog/pg_range.h"
  #include "catalog/pg_statistic.h"
  #include "catalog/pg_transform.h"
- #include "catalog/pg_tablesample_method.h"
  #include "catalog/pg_type.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
--- 32,37 ----
*************** get_range_subtype(Oid rangeOid)
*** 2997,3025 ****
      else
          return InvalidOid;
  }
-
- /*                ---------- PG_TABLESAMPLE_METHOD CACHE ----------             */
-
- /*
-  * get_tablesample_method_name - given a tablesample method OID,
-  * look up the name or NULL if not found
-  */
- char *
- get_tablesample_method_name(Oid tsmid)
- {
-     HeapTuple    tuple;
-
-     tuple = SearchSysCache1(TABLESAMPLEMETHODOID, ObjectIdGetDatum(tsmid));
-     if (HeapTupleIsValid(tuple))
-     {
-         Form_pg_tablesample_method tup =
-         (Form_pg_tablesample_method) GETSTRUCT(tuple);
-         char       *result;
-
-         result = pstrdup(NameStr(tup->tsmname));
-         ReleaseSysCache(tuple);
-         return result;
-     }
-     else
-         return NULL;
- }
--- 2996,2998 ----
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index b6333e3..efce7b9 100644
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 56,62 ****
  #include "catalog/pg_shseclabel.h"
  #include "catalog/pg_replication_origin.h"
  #include "catalog/pg_statistic.h"
- #include "catalog/pg_tablesample_method.h"
  #include "catalog/pg_tablespace.h"
  #include "catalog/pg_transform.h"
  #include "catalog/pg_ts_config.h"
--- 56,61 ----
*************** static const struct cachedesc cacheinfo[
*** 667,694 ****
          },
          128
      },
-     {TableSampleMethodRelationId,        /* TABLESAMPLEMETHODNAME */
-         TableSampleMethodNameIndexId,
-         1,
-         {
-             Anum_pg_tablesample_method_tsmname,
-             0,
-             0,
-             0,
-         },
-         2
-     },
-     {TableSampleMethodRelationId,        /* TABLESAMPLEMETHODOID */
-         TableSampleMethodOidIndexId,
-         1,
-         {
-             ObjectIdAttributeNumber,
-             0,
-             0,
-             0,
-         },
-         2
-     },
      {TableSpaceRelationId,        /* TABLESPACEOID */
          TablespaceOidIndexId,
          1,
--- 666,671 ----
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index 6cc3ed9..6f2fb4d 100644
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
*************** Section: Class 22 - Data Exception
*** 178,183 ****
--- 178,185 ----
  2201W    E    ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE                      invalid_row_count_in_limit_clause
  2201X    E    ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE
invalid_row_count_in_result_offset_clause
  22009    E    ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE                   invalid_time_zone_displacement_value
+ 2202H    E    ERRCODE_INVALID_TABLESAMPLE_ARGUMENT                           invalid_tablesample_argument
+ 2202G    E    ERRCODE_INVALID_TABLESAMPLE_REPEAT                             invalid_tablesample_repeat
  2200C    E    ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER                        invalid_use_of_escape_character
  2200G    E    ERRCODE_MOST_SPECIFIC_TYPE_MISMATCH                            most_specific_type_mismatch
  22004    E    ERRCODE_NULL_VALUE_NOT_ALLOWED                                 null_value_not_allowed
diff --git a/src/backend/utils/misc/sampling.c b/src/backend/utils/misc/sampling.c
index 6191f79..4142e01 100644
*** a/src/backend/utils/misc/sampling.c
--- b/src/backend/utils/misc/sampling.c
*************** reservoir_get_next_S(ReservoirState rs,
*** 228,234 ****
  void
  sampler_random_init_state(long seed, SamplerRandomState randstate)
  {
!     randstate[0] = RAND48_SEED_0;
      randstate[1] = (unsigned short) seed;
      randstate[2] = (unsigned short) (seed >> 16);
  }
--- 228,234 ----
  void
  sampler_random_init_state(long seed, SamplerRandomState randstate)
  {
!     randstate[0] = 0x330e;        /* same as pg_erand48, but could be anything */
      randstate[1] = (unsigned short) seed;
      randstate[2] = (unsigned short) (seed >> 16);
  }
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9596af6..ece0515 100644
*** a/src/bin/psql/tab-complete.c
--- b/src/bin/psql/tab-complete.c
*************** static const SchemaQuery Query_for_list_
*** 738,750 ****
  "  WHERE substring(pg_catalog.quote_ident(evtname),1,%d)='%s'"

  #define Query_for_list_of_tablesample_methods \
! " SELECT pg_catalog.quote_ident(tsmname) "\
! "   FROM pg_catalog.pg_tablesample_method "\
! "  WHERE substring(pg_catalog.quote_ident(tsmname),1,%d)='%s'"

  #define Query_for_list_of_policies \
  " SELECT pg_catalog.quote_ident(polname) "\
! "   FROM pg_catalog.pg_policy " \
  "  WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'"

  #define Query_for_list_of_tables_for_policy \
--- 738,752 ----
  "  WHERE substring(pg_catalog.quote_ident(evtname),1,%d)='%s'"

  #define Query_for_list_of_tablesample_methods \
! " SELECT pg_catalog.quote_ident(proname) "\
! "   FROM pg_catalog.pg_proc "\
! "  WHERE prorettype = 'pg_catalog.tsm_handler'::pg_catalog.regtype AND "\
! "        proargtypes[0] = 'pg_catalog.internal'::pg_catalog.regtype AND "\
! "        substring(pg_catalog.quote_ident(proname),1,%d)='%s'"

  #define Query_for_list_of_policies \
  " SELECT pg_catalog.quote_ident(polname) "\
! "   FROM pg_catalog.pg_policy "\
  "  WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'"

  #define Query_for_list_of_tables_for_policy \
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 31139cb..75e6b72 100644
*** a/src/include/access/heapam.h
--- b/src/include/access/heapam.h
*************** extern HeapScanDesc heap_beginscan_bm(Re
*** 116,126 ****
                    int nkeys, ScanKey key);
  extern HeapScanDesc heap_beginscan_sampling(Relation relation,
                          Snapshot snapshot, int nkeys, ScanKey key,
!                         bool allow_strat, bool allow_pagemode);
  extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
                     BlockNumber endBlk);
  extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
  extern void heap_rescan(HeapScanDesc scan, ScanKey key);
  extern void heap_endscan(HeapScanDesc scan);
  extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);

--- 116,128 ----
                    int nkeys, ScanKey key);
  extern HeapScanDesc heap_beginscan_sampling(Relation relation,
                          Snapshot snapshot, int nkeys, ScanKey key,
!                      bool allow_strat, bool allow_sync, bool allow_pagemode);
  extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
                     BlockNumber endBlk);
  extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
  extern void heap_rescan(HeapScanDesc scan, ScanKey key);
+ extern void heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
+                      bool allow_strat, bool allow_sync, bool allow_pagemode);
  extern void heap_endscan(HeapScanDesc scan);
  extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);

diff --git a/src/include/access/tablesample.h b/src/include/access/tablesample.h
index a02e93d..e69de29 100644
*** a/src/include/access/tablesample.h
--- b/src/include/access/tablesample.h
***************
*** 1,61 ****
- /*-------------------------------------------------------------------------
-  *
-  * tablesample.h
-  *          Public header file for TABLESAMPLE clause interface
-  *
-  *
-  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
-  * Portions Copyright (c) 1994, Regents of the University of California
-  *
-  * src/include/access/tablesample.h
-  *
-  *-------------------------------------------------------------------------
-  */
- #ifndef TABLESAMPLE_H
- #define TABLESAMPLE_H
-
- #include "access/relscan.h"
- #include "executor/executor.h"
-
- typedef struct TableSampleDesc
- {
-     HeapScanDesc heapScan;
-     TupleDesc    tupDesc;        /* Mostly useful for tsmexaminetuple */
-
-     void       *tsmdata;        /* private method data */
-
-     /* These point to he function of the TABLESAMPLE Method. */
-     FmgrInfo    tsminit;
-     FmgrInfo    tsmnextblock;
-     FmgrInfo    tsmnexttuple;
-     FmgrInfo    tsmexaminetuple;
-     FmgrInfo    tsmreset;
-     FmgrInfo    tsmend;
- } TableSampleDesc;
-
-
- extern TableSampleDesc *tablesample_init(SampleScanState *scanstate,
-                  TableSampleClause *tablesample);
- extern HeapTuple tablesample_getnext(TableSampleDesc *desc);
- extern void tablesample_reset(TableSampleDesc *desc);
- extern void tablesample_end(TableSampleDesc *desc);
- extern HeapTuple tablesample_source_getnext(TableSampleDesc *desc);
- extern HeapTuple tablesample_source_gettup(TableSampleDesc *desc, ItemPointer tid,
-                           bool *visible);
-
- extern Datum tsm_system_init(PG_FUNCTION_ARGS);
- extern Datum tsm_system_nextblock(PG_FUNCTION_ARGS);
- extern Datum tsm_system_nexttuple(PG_FUNCTION_ARGS);
- extern Datum tsm_system_end(PG_FUNCTION_ARGS);
- extern Datum tsm_system_reset(PG_FUNCTION_ARGS);
- extern Datum tsm_system_cost(PG_FUNCTION_ARGS);
-
- extern Datum tsm_bernoulli_init(PG_FUNCTION_ARGS);
- extern Datum tsm_bernoulli_nextblock(PG_FUNCTION_ARGS);
- extern Datum tsm_bernoulli_nexttuple(PG_FUNCTION_ARGS);
- extern Datum tsm_bernoulli_end(PG_FUNCTION_ARGS);
- extern Datum tsm_bernoulli_reset(PG_FUNCTION_ARGS);
- extern Datum tsm_bernoulli_cost(PG_FUNCTION_ARGS);
-
-
- #endif
--- 0 ----
diff --git a/src/include/access/tsmapi.h b/src/include/access/tsmapi.h
index ...c3b0cab .
*** a/src/include/access/tsmapi.h
--- b/src/include/access/tsmapi.h
***************
*** 0 ****
--- 1,81 ----
+ /*-------------------------------------------------------------------------
+  *
+  * tsmapi.h
+  *      API for tablesample methods
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * src/include/access/tsmapi.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef TSMAPI_H
+ #define TSMAPI_H
+
+ #include "nodes/execnodes.h"
+ #include "nodes/relation.h"
+
+
+ /*
+  * Callback function signatures --- see tablesample-method.sgml for more info.
+  */
+
+ typedef void (*SampleScanCost_function) (PlannerInfo *root,
+                                                      RelOptInfo *baserel,
+                                                      List *paramexprs,
+                                                      BlockNumber *pages,
+                                                      double *tuples);
+
+ typedef void (*InitSampleScan_function) (SampleScanState *node,
+                                                      int eflags);
+
+ typedef void (*BeginSampleScan_function) (SampleScanState *node,
+                                                       Datum *params,
+                                                       int nparams,
+                                                       uint32 seed);
+
+ typedef BlockNumber (*NextSampleBlock_function) (SampleScanState *node);
+
+ typedef OffsetNumber (*NextSampleTuple_function) (SampleScanState *node,
+                                                          BlockNumber blockno,
+                                                      OffsetNumber maxoffset);
+
+ typedef void (*EndSampleScan_function) (SampleScanState *node);
+
+ /*
+  * TsmRoutine is the struct returned by a tablesample method's handler
+  * function.  It provides pointers to the callback functions needed by the
+  * planner and executor, as well as additional information about the method.
+  *
+  * More function pointers are likely to be added in the future.
+  * Therefore it's recommended that the handler initialize the struct with
+  * makeNode(TsmRoutine) so that all fields are set to NULL.  This will
+  * ensure that no fields are accidentally left undefined.
+  */
+ typedef struct TsmRoutine
+ {
+     NodeTag        type;
+
+     /* List of datatype OIDs for the arguments of the TABLESAMPLE clause */
+     List       *parameterTypes;
+
+     /* Can method produce repeatable samples across, or even within, queries? */
+     bool        repeatable_across_queries;
+     bool        repeatable_across_scans;
+
+     /* Functions for planning a SampleScan on a physical table */
+     SampleScanCost_function SampleScanCost;
+
+     /* Functions for executing a SampleScan on a physical table */
+     InitSampleScan_function InitSampleScan;        /* can be NULL */
+     BeginSampleScan_function BeginSampleScan;
+     NextSampleBlock_function NextSampleBlock;    /* can be NULL */
+     NextSampleTuple_function NextSampleTuple;
+     EndSampleScan_function EndSampleScan;        /* can be NULL */
+ } TsmRoutine;
+
+
+ /* Functions in access/tablesample/tablesample.c */
+ extern TsmRoutine *GetTsmRoutine(Oid tsmhandler);
+
+ #endif   /* TSMAPI_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 748aadd..c38958d 100644
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
*************** DECLARE_UNIQUE_INDEX(pg_replication_orig
*** 316,326 ****
  DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication_origin using btree(roname
text_pattern_ops));
  #define ReplicationOriginNameIndex 6002

- DECLARE_UNIQUE_INDEX(pg_tablesample_method_name_index, 3331, on pg_tablesample_method using btree(tsmname name_ops));
- #define TableSampleMethodNameIndexId  3331
- DECLARE_UNIQUE_INDEX(pg_tablesample_method_oid_index, 3332, on pg_tablesample_method using btree(oid oid_ops));
- #define TableSampleMethodOidIndexId  3332
-
  /* last step of initialization script: build the indexes declared above */
  BUILD_INDICES

--- 316,321 ----
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 1d68ad7..09bf143 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 3116 (  fdw_handler_in
*** 3734,3739 ****
--- 3734,3749 ----
  DESCR("I/O");
  DATA(insert OID = 3117 (  fdw_handler_out    PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3115" _null_ _null_
_null__null_ _null_ fdw_handler_out _null_ _null_ _null_ )); 
  DESCR("I/O");
+ DATA(insert OID = 3311 (  tsm_handler_in    PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3310 "2275" _null_ _null_ _null_
_null__null_ tsm_handler_in _null_ _null_ _null_ )); 
+ DESCR("I/O");
+ DATA(insert OID = 3312 (  tsm_handler_out    PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3310" _null_ _null_
_null__null_ _null_ tsm_handler_out _null_ _null_ _null_ )); 
+ DESCR("I/O");
+
+ /* tablesample method handlers */
+ DATA(insert OID = 3313 (  bernoulli            PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 3310 "2281" _null_ _null_
_null__null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ )); 
+ DESCR("BERNOULLI tablesample method handler");
+ DATA(insert OID = 3314 (  system            PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 3310 "2281" _null_ _null_ _null_
_null__null_ tsm_system_handler _null_ _null_ _null_ )); 
+ DESCR("SYSTEM tablesample method handler");

  /* cryptographic */
  DATA(insert OID =  2311 (  md5       PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "25" _null_ _null_ _null_ _null_
_null_md5_text _null_ _null_ _null_ )); 
*************** DESCR("get an individual replication ori
*** 5321,5353 ****
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v 0 0 2249 ""
"{26,25,3220,3220}""{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_
pg_show_replication_origin_status_null_ _null_ _null_ )); 
  DESCR("get progress for all replication origins");

- /* tablesample */
- DATA(insert OID = 3335 (  tsm_system_init        PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 2278 "2281 23 700" _null_
_null__null_ _null_ _null_ tsm_system_init _null_ _null_ _null_ )); 
- DESCR("tsm_system_init(internal)");
- DATA(insert OID = 3336 (  tsm_system_nextblock    PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "2281 16" _null_ _null_
_null__null_ _null_ tsm_system_nextblock _null_ _null_ _null_ )); 
- DESCR("tsm_system_nextblock(internal)");
- DATA(insert OID = 3337 (  tsm_system_nexttuple    PGNSP PGUID 12 1 0 0 0 f f f f t f v 4 0 21 "2281 23 21 16" _null_
_null__null_ _null_ _null_ tsm_system_nexttuple _null_ _null_ _null_ )); 
- DESCR("tsm_system_nexttuple(internal)");
- DATA(insert OID = 3338 (  tsm_system_end        PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "2281" _null_ _null_
_null__null_ _null_ tsm_system_end _null_ _null_ _null_ )); 
- DESCR("tsm_system_end(internal)");
- DATA(insert OID = 3339 (  tsm_system_reset        PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "2281" _null_ _null_
_null__null_ _null_ tsm_system_reset _null_ _null_ _null_ )); 
- DESCR("tsm_system_reset(internal)");
- DATA(insert OID = 3340 (  tsm_system_cost        PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281 2281
22812281 2281" _null_ _null_ _null_ _null_ _null_ tsm_system_cost _null_ _null_ _null_ )); 
- DESCR("tsm_system_cost(internal)");
-
- DATA(insert OID = 3341 (  tsm_bernoulli_init        PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 2278 "2281 23 700"
_null__null_ _null_ _null_ _null_ tsm_bernoulli_init _null_ _null_ _null_ )); 
- DESCR("tsm_bernoulli_init(internal)");
- DATA(insert OID = 3342 (  tsm_bernoulli_nextblock    PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 23 "2281 16" _null_
_null__null_ _null_ _null_ tsm_bernoulli_nextblock _null_ _null_ _null_ )); 
- DESCR("tsm_bernoulli_nextblock(internal)");
- DATA(insert OID = 3343 (  tsm_bernoulli_nexttuple    PGNSP PGUID 12 1 0 0 0 f f f f t f v 4 0 21 "2281 23 21 16"
_null__null_ _null_ _null_ _null_ tsm_bernoulli_nexttuple _null_ _null_ _null_ )); 
- DESCR("tsm_bernoulli_nexttuple(internal)");
- DATA(insert OID = 3344 (  tsm_bernoulli_end            PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "2281" _null_
_null__null_ _null_ _null_ tsm_bernoulli_end _null_ _null_ _null_ )); 
- DESCR("tsm_bernoulli_end(internal)");
- DATA(insert OID = 3345 (  tsm_bernoulli_reset        PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2278 "2281" _null_
_null__null_ _null_ _null_ tsm_bernoulli_reset _null_ _null_ _null_ )); 
- DESCR("tsm_bernoulli_reset(internal)");
- DATA(insert OID = 3346 (  tsm_bernoulli_cost        PGNSP PGUID 12 1 0 0 0 f f f f t f v 7 0 2278 "2281 2281 2281
22812281 2281 2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_cost _null_ _null_ _null_ )); 
- DESCR("tsm_bernoulli_cost(internal)");
-
  /*
   * Symbolic values for provolatile column: these indicate whether the result
   * of a function is dependent *only* on the values of its explicit arguments,
--- 5331,5336 ----
diff --git a/src/include/catalog/pg_tablesample_method.h b/src/include/catalog/pg_tablesample_method.h
index b422414..e69de29 100644
*** a/src/include/catalog/pg_tablesample_method.h
--- b/src/include/catalog/pg_tablesample_method.h
***************
*** 1,81 ****
- /*-------------------------------------------------------------------------
-  *
-  * pg_tablesample_method.h
-  *      definition of the table scan methods.
-  *
-  *
-  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
-  * Portions Copyright (c) 1994, Regents of the University of California
-  *
-  * src/include/catalog/pg_tablesample_method.h
-  *
-  *
-  *-------------------------------------------------------------------------
-  */
- #ifndef PG_TABLESAMPLE_METHOD_H
- #define PG_TABLESAMPLE_METHOD_H
-
- #include "catalog/genbki.h"
- #include "catalog/objectaddress.h"
-
- /* ----------------
-  *        pg_tablesample_method definition.  cpp turns this into
-  *        typedef struct FormData_pg_tablesample_method
-  * ----------------
-  */
- #define TableSampleMethodRelationId 3330
-
- CATALOG(pg_tablesample_method,3330)
- {
-     NameData    tsmname;        /* tablesample method name */
-     bool        tsmseqscan;        /* does this method scan whole table
-                                  * sequentially? */
-     bool        tsmpagemode;    /* does this method scan page at a time? */
-     regproc        tsminit;        /* init scan function */
-     regproc        tsmnextblock;    /* function returning next block to sample or
-                                  * InvalidBlockOffset if finished */
-     regproc        tsmnexttuple;    /* function returning next tuple offset from
-                                  * current block or InvalidOffsetNumber if end
-                                  * of the block was reacher */
-     regproc        tsmexaminetuple;/* optional function which can examine tuple
-                                  * contents and decide if tuple should be
-                                  * returned or not */
-     regproc        tsmend;            /* end scan function */
-     regproc        tsmreset;        /* reset state - used by rescan */
-     regproc        tsmcost;        /* costing function */
- } FormData_pg_tablesample_method;
-
- /* ----------------
-  *        Form_pg_tablesample_method corresponds to a pointer to a tuple with
-  *        the format of pg_tablesample_method relation.
-  * ----------------
-  */
- typedef FormData_pg_tablesample_method *Form_pg_tablesample_method;
-
- /* ----------------
-  *        compiler constants for pg_tablesample_method
-  * ----------------
-  */
- #define Natts_pg_tablesample_method                    10
- #define Anum_pg_tablesample_method_tsmname            1
- #define Anum_pg_tablesample_method_tsmseqscan        2
- #define Anum_pg_tablesample_method_tsmpagemode        3
- #define Anum_pg_tablesample_method_tsminit            4
- #define Anum_pg_tablesample_method_tsmnextblock        5
- #define Anum_pg_tablesample_method_tsmnexttuple        6
- #define Anum_pg_tablesample_method_tsmexaminetuple    7
- #define Anum_pg_tablesample_method_tsmend            8
- #define Anum_pg_tablesample_method_tsmreset            9
- #define Anum_pg_tablesample_method_tsmcost            10
-
- /* ----------------
-  *        initial contents of pg_tablesample_method
-  * ----------------
-  */
-
- DATA(insert OID = 3333 ( system false true tsm_system_init tsm_system_nextblock tsm_system_nexttuple - tsm_system_end
tsm_system_resettsm_system_cost )); 
- DESCR("SYSTEM table sampling method");
- DATA(insert OID = 3334 ( bernoulli true false tsm_bernoulli_init tsm_bernoulli_nextblock tsm_bernoulli_nexttuple -
tsm_bernoulli_endtsm_bernoulli_reset tsm_bernoulli_cost )); 
- DESCR("BERNOULLI table sampling method");
-
- #endif   /* PG_TABLESAMPLE_METHOD_H */
--- 0 ----
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index da123f6..7dc95c8 100644
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
*************** DATA(insert OID = 3500 ( anyenum        PGNSP
*** 694,699 ****
--- 694,701 ----
  #define ANYENUMOID        3500
  DATA(insert OID = 3115 ( fdw_handler    PGNSP PGUID  4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - -
ip f 0 -1 0 0 _null_ _null_ _null_ )); 
  #define FDW_HANDLEROID    3115
+ DATA(insert OID = 3310 ( tsm_handler    PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - -
ip f 0 -1 0 0 _null_ _null_ _null_ )); 
+ #define TSM_HANDLEROID    3310
  DATA(insert OID = 3831 ( anyrange        PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x
f0 -1 0 0 _null_ _null_ _null_ )); 
  #define ANYRANGEOID        3831

diff --git a/src/include/executor/nodeSamplescan.h b/src/include/executor/nodeSamplescan.h
index 4b769da..a0cc6ce 100644
*** a/src/include/executor/nodeSamplescan.h
--- b/src/include/executor/nodeSamplescan.h
***************
*** 4,10 ****
   *
   *
   *
!  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   * src/include/executor/nodeSamplescan.h
--- 4,10 ----
   *
   *
   *
!  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   * src/include/executor/nodeSamplescan.h
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 541ee18..303fc3c 100644
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
*************** typedef struct ScanState
*** 1257,1269 ****
   */
  typedef ScanState SeqScanState;

! /*
!  * SampleScan
   */
  typedef struct SampleScanState
  {
      ScanState    ss;
!     struct TableSampleDesc *tsdesc;
  } SampleScanState;

  /*
--- 1257,1278 ----
   */
  typedef ScanState SeqScanState;

! /* ----------------
!  *     SampleScanState information
!  * ----------------
   */
  typedef struct SampleScanState
  {
      ScanState    ss;
!     List       *args;            /* expr states for TABLESAMPLE params */
!     ExprState  *repeatable;        /* expr state for REPEATABLE expr */
!     /* use struct pointer to avoid including tsmapi.h here */
!     struct TsmRoutine *tsmroutine;        /* descriptor for tablesample method */
!     void       *tsm_state;        /* tablesample method can keep state here */
!     bool        use_bulkread;    /* use bulkread buffer access strategy? */
!     bool        use_pagemode;    /* use page-at-a-time visibility checking? */
!     bool        begun;            /* false means need to call BeginSampleScan */
!     uint32        seed;            /* random seed */
  } SampleScanState;

  /*
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f8acda4..748e434 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 51,56 ****
--- 51,57 ----
      T_BitmapOr,
      T_Scan,
      T_SeqScan,
+     T_SampleScan,
      T_IndexScan,
      T_IndexOnlyScan,
      T_BitmapIndexScan,
*************** typedef enum NodeTag
*** 61,67 ****
      T_ValuesScan,
      T_CteScan,
      T_WorkTableScan,
-     T_SampleScan,
      T_ForeignScan,
      T_CustomScan,
      T_Join,
--- 62,67 ----
*************** typedef enum NodeTag
*** 400,405 ****
--- 400,406 ----
      T_WindowDef,
      T_RangeSubselect,
      T_RangeFunction,
+     T_RangeTableSample,
      T_TypeName,
      T_ColumnDef,
      T_IndexElem,
*************** typedef enum NodeTag
*** 407,412 ****
--- 408,414 ----
      T_DefElem,
      T_RangeTblEntry,
      T_RangeTblFunction,
+     T_TableSampleClause,
      T_WithCheckOption,
      T_SortGroupClause,
      T_GroupingSet,
*************** typedef enum NodeTag
*** 425,432 ****
      T_OnConflictClause,
      T_CommonTableExpr,
      T_RoleSpec,
-     T_RangeTableSample,
-     T_TableSampleClause,

      /*
       * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
--- 427,432 ----
*************** typedef enum NodeTag
*** 452,458 ****
      T_WindowObjectData,            /* private in nodeWindowAgg.c */
      T_TIDBitmap,                /* in nodes/tidbitmap.h */
      T_InlineCodeBlock,            /* in nodes/parsenodes.h */
!     T_FdwRoutine                /* in foreign/fdwapi.h */
  } NodeTag;

  /*
--- 452,459 ----
      T_WindowObjectData,            /* private in nodeWindowAgg.c */
      T_TIDBitmap,                /* in nodes/tidbitmap.h */
      T_InlineCodeBlock,            /* in nodes/parsenodes.h */
!     T_FdwRoutine,                /* in foreign/fdwapi.h */
!     T_TsmRoutine                /* in access/tsmapi.h */
  } NodeTag;

  /*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b336ff9..151c93a 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct FuncCall
*** 338,363 ****
  } FuncCall;

  /*
-  * TableSampleClause - a sampling method information
-  */
- typedef struct TableSampleClause
- {
-     NodeTag        type;
-     Oid            tsmid;
-     bool        tsmseqscan;
-     bool        tsmpagemode;
-     Oid            tsminit;
-     Oid            tsmnextblock;
-     Oid            tsmnexttuple;
-     Oid            tsmexaminetuple;
-     Oid            tsmend;
-     Oid            tsmreset;
-     Oid            tsmcost;
-     Node       *repeatable;
-     List       *args;
- } TableSampleClause;
-
- /*
   * A_Star - '*' representing all columns of a table or compound field
   *
   * This can appear within ColumnRef.fields, A_Indirection.indirection, and
--- 338,343 ----
*************** typedef struct RangeFunction
*** 558,576 ****
  } RangeFunction;

  /*
!  * RangeTableSample - represents <table> TABLESAMPLE <method> (<params>) REPEATABLE (<num>)
   *
!  * SQL Standard specifies only one parameter which is percentage. But we allow
!  * custom tablesample methods which may need different input arguments so we
!  * accept list of arguments.
   */
  typedef struct RangeTableSample
  {
      NodeTag        type;
!     RangeVar   *relation;
!     char       *method;            /* sampling method */
!     Node       *repeatable;
!     List       *args;            /* arguments for sampling method */
  } RangeTableSample;

  /*
--- 538,560 ----
  } RangeFunction;

  /*
!  * RangeTableSample - TABLESAMPLE appearing in a raw FROM clause
   *
!  * This node, appearing only in raw parse trees, represents
!  *        <relation> TABLESAMPLE <method> (<params>) REPEATABLE (<num>)
!  * Currently, the <relation> can only be a RangeVar, but we might in future
!  * allow RangeSubselect and other options.  Note that the RangeTableSample
!  * is wrapped around the node representing the <relation>, rather than being
!  * a subfield of it.
   */
  typedef struct RangeTableSample
  {
      NodeTag        type;
!     Node       *relation;        /* relation to be sampled */
!     List       *method;            /* sampling method name (possibly qualified) */
!     List       *args;            /* argument(s) for sampling method */
!     Node       *repeatable;        /* REPEATABLE expression, or NULL if none */
!     int            location;        /* method name location, or -1 if unknown */
  } RangeTableSample;

  /*
*************** typedef struct RangeTblEntry
*** 810,816 ****
       */
      Oid            relid;            /* OID of the relation */
      char        relkind;        /* relation kind (see pg_class.relkind) */
!     TableSampleClause *tablesample;        /* sampling method and parameters */

      /*
       * Fields valid for a subquery RTE (else NULL):
--- 794,800 ----
       */
      Oid            relid;            /* OID of the relation */
      char        relkind;        /* relation kind (see pg_class.relkind) */
!     struct TableSampleClause *tablesample;        /* sampling info, or NULL */

      /*
       * Fields valid for a subquery RTE (else NULL):
*************** typedef struct RangeTblFunction
*** 913,918 ****
--- 897,915 ----
  } RangeTblFunction;

  /*
+  * TableSampleClause - TABLESAMPLE appearing in a transformed FROM clause
+  *
+  * Unlike RangeTableSample, this is a subnode of the relevant RangeTblEntry.
+  */
+ typedef struct TableSampleClause
+ {
+     NodeTag        type;
+     Oid            tsmhandler;        /* OID of the tablesample handler function */
+     List       *args;            /* tablesample argument expression(s) */
+     Expr       *repeatable;        /* REPEATABLE expression, or NULL if none */
+ } TableSampleClause;
+
+ /*
   * WithCheckOption -
   *        representation of WITH CHECK OPTION checks to be applied to new tuples
   *        when inserting/updating an auto-updatable view, or RLS WITH CHECK
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 5f538f3..0654d02 100644
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
*************** typedef Scan SeqScan;
*** 287,293 ****
   *        table sample scan node
   * ----------------
   */
! typedef Scan SampleScan;

  /* ----------------
   *        index scan node
--- 287,298 ----
   *        table sample scan node
   * ----------------
   */
! typedef struct SampleScan
! {
!     Scan        scan;
!     /* use struct pointer to avoid including parsenodes.h here */
!     struct TableSampleClause *tablesample;
! } SampleScan;

  /* ----------------
   *        index scan node
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 24003ae..dd43e45 100644
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
*************** extern double index_pages_fetched(double
*** 68,74 ****
                      double index_pages, PlannerInfo *root);
  extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
               ParamPathInfo *param_info);
! extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
  extern void cost_index(IndexPath *path, PlannerInfo *root,
             double loop_count);
  extern void cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
--- 68,75 ----
                      double index_pages, PlannerInfo *root);
  extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
               ParamPathInfo *param_info);
! extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
!                 ParamPathInfo *param_info);
  extern void cost_index(IndexPath *path, PlannerInfo *root,
             double loop_count);
  extern void cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 3194da4..3264691 100644
*** a/src/include/parser/parse_func.h
--- b/src/include/parser/parse_func.h
*************** typedef enum
*** 33,43 ****
  extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                    FuncCall *fn, int location);

- extern TableSampleClause *ParseTableSample(ParseState *pstate,
-                  char *samplemethod,
-                  Node *repeatable, List *args,
-                  int location);
-
  extern FuncDetailCode func_get_detail(List *funcname,
                  List *fargs, List *fargnames,
                  int nargs, Oid *argtypes,
--- 33,38 ----
diff --git a/src/include/port.h b/src/include/port.h
index 71113c0..3787cbf 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern off_t ftello(FILE *stream);
*** 357,366 ****
  #endif
  #endif

- #define RAND48_SEED_0    (0x330e)
- #define RAND48_SEED_1    (0xabcd)
- #define RAND48_SEED_2    (0x1234)
-
  extern double pg_erand48(unsigned short xseed[3]);
  extern long pg_lrand48(void);
  extern void pg_srand48(long seed);
--- 357,362 ----
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index fcb0bf0..49caa56 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum language_handler_in(PG_FUNC
*** 566,571 ****
--- 566,573 ----
  extern Datum language_handler_out(PG_FUNCTION_ARGS);
  extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
  extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
+ extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
+ extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
  extern Datum internal_in(PG_FUNCTION_ARGS);
  extern Datum internal_out(PG_FUNCTION_ARGS);
  extern Datum opaque_in(PG_FUNCTION_ARGS);
*************** extern Datum ginqueryarrayextract(PG_FUN
*** 1213,1218 ****
--- 1215,1226 ----
  extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
  extern Datum ginarraytriconsistent(PG_FUNCTION_ARGS);

+ /* access/tablesample/bernoulli.c */
+ extern Datum tsm_bernoulli_handler(PG_FUNCTION_ARGS);
+
+ /* access/tablesample/system.c */
+ extern Datum tsm_system_handler(PG_FUNCTION_ARGS);
+
  /* access/transam/twophase.c */
  extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);

diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index a40c9b1..9711538 100644
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
*************** extern void free_attstatsslot(Oid atttyp
*** 156,162 ****
  extern char *get_namespace_name(Oid nspid);
  extern char *get_namespace_name_or_temp(Oid nspid);
  extern Oid    get_range_subtype(Oid rangeOid);
- extern char *get_tablesample_method_name(Oid tsmid);

  #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
  /* type_is_array_domain accepts both plain arrays and domains over arrays */
--- 156,161 ----
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f06f03a..18404e2 100644
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
*************** enum SysCacheIdentifier
*** 81,88 ****
      REPLORIGNAME,
      RULERELNAME,
      STATRELATTINH,
-     TABLESAMPLEMETHODNAME,
-     TABLESAMPLEMETHODOID,
      TABLESPACEOID,
      TRFOID,
      TRFTYPELANG,
--- 81,86 ----
diff --git a/src/port/erand48.c b/src/port/erand48.c
index 12efd81..9d47119 100644
*** a/src/port/erand48.c
--- b/src/port/erand48.c
***************
*** 33,38 ****
--- 33,41 ----

  #include <math.h>

+ #define RAND48_SEED_0    (0x330e)
+ #define RAND48_SEED_1    (0xabcd)
+ #define RAND48_SEED_2    (0x1234)
  #define RAND48_MULT_0    (0xe66d)
  #define RAND48_MULT_1    (0xdeec)
  #define RAND48_MULT_2    (0x0005)
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index eabfd93..343c4fb 100644
*** a/src/test/regress/expected/rowsecurity.out
--- b/src/test/regress/expected/rowsecurity.out
*************** NOTICE:  f_leak => great manga
*** 101,115 ****
    44 |   8 |      1 | rls_regress_user2 | great manga           | manga
  (4 rows)

! SELECT * FROM document TABLESAMPLE BERNOULLI (50) REPEATABLE(1) WHERE f_leak(dtitle) ORDER BY did;
! NOTICE:  f_leak => my first novel
  NOTICE:  f_leak => my first manga
  NOTICE:  f_leak => great science fiction
   did | cid | dlevel |      dauthor      |        dtitle
  -----+-----+--------+-------------------+-----------------------
-    1 |  11 |      1 | rls_regress_user1 | my first novel
     4 |  44 |      1 | rls_regress_user1 | my first manga
     6 |  22 |      1 | rls_regress_user2 | great science fiction
  (3 rows)

  -- viewpoint from rls_regress_user2
--- 101,117 ----
    44 |   8 |      1 | rls_regress_user2 | great manga           | manga
  (4 rows)

! -- try a sampled version
! SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
!   WHERE f_leak(dtitle) ORDER BY did;
  NOTICE:  f_leak => my first manga
  NOTICE:  f_leak => great science fiction
+ NOTICE:  f_leak => great manga
   did | cid | dlevel |      dauthor      |        dtitle
  -----+-----+--------+-------------------+-----------------------
     4 |  44 |      1 | rls_regress_user1 | my first manga
     6 |  22 |      1 | rls_regress_user2 | great science fiction
+    8 |  44 |      1 | rls_regress_user2 | great manga
  (3 rows)

  -- viewpoint from rls_regress_user2
*************** NOTICE:  f_leak => great manga
*** 156,175 ****
    44 |   8 |      1 | rls_regress_user2 | great manga           | manga
  (8 rows)

! SELECT * FROM document TABLESAMPLE BERNOULLI (50) REPEATABLE(1) WHERE f_leak(dtitle) ORDER BY did;
! NOTICE:  f_leak => my first novel
! NOTICE:  f_leak => my second novel
  NOTICE:  f_leak => my first manga
  NOTICE:  f_leak => great science fiction
! NOTICE:  f_leak => great technology book
   did | cid | dlevel |      dauthor      |        dtitle
  -----+-----+--------+-------------------+-----------------------
-    1 |  11 |      1 | rls_regress_user1 | my first novel
-    2 |  11 |      2 | rls_regress_user1 | my second novel
     4 |  44 |      1 | rls_regress_user1 | my first manga
     6 |  22 |      1 | rls_regress_user2 | great science fiction
!    7 |  33 |      2 | rls_regress_user2 | great technology book
! (5 rows)

  EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
                          QUERY PLAN
--- 158,177 ----
    44 |   8 |      1 | rls_regress_user2 | great manga           | manga
  (8 rows)

! -- try a sampled version
! SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
!   WHERE f_leak(dtitle) ORDER BY did;
  NOTICE:  f_leak => my first manga
+ NOTICE:  f_leak => my second manga
  NOTICE:  f_leak => great science fiction
! NOTICE:  f_leak => great manga
   did | cid | dlevel |      dauthor      |        dtitle
  -----+-----+--------+-------------------+-----------------------
     4 |  44 |      1 | rls_regress_user1 | my first manga
+    5 |  44 |      2 | rls_regress_user1 | my second manga
     6 |  22 |      1 | rls_regress_user2 | great science fiction
!    8 |  44 |      1 | rls_regress_user2 | great manga
! (4 rows)

  EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
                          QUERY PLAN
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 14acd16..eb0bc88 100644
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** pg_shdepend|t
*** 128,134 ****
  pg_shdescription|t
  pg_shseclabel|t
  pg_statistic|t
- pg_tablesample_method|t
  pg_tablespace|t
  pg_transform|t
  pg_trigger|t
--- 128,133 ----
diff --git a/src/test/regress/expected/tablesample.out b/src/test/regress/expected/tablesample.out
index 04e5eb8..23ea8cb 100644
*** a/src/test/regress/expected/tablesample.out
--- b/src/test/regress/expected/tablesample.out
***************
*** 1,107 ****
! CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- force smaller pages so we don't have to
loadtoo much data to get multiple pages 
! INSERT INTO test_tablesample SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i) ORDER BY i;
! SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (10);
   id
  ----
-   0
-   1
-   2
    3
    4
    5
-   9
- (7 rows)
-
- SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0/11) REPEATABLE (9999);
-  id
- ----
    6
    7
    8
! (3 rows)

! SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100);
   count
  -------
      10
  (1 row)

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (100);
   id
  ----
!   0
!   1
!   2
    6
    7
    8
!   9
! (7 rows)

! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (100);
   id
  ----
-   0
-   1
-   3
    4
    5
  (5 rows)

! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1);
   id
  ----
!   0
!   5
! (2 rows)

  CREATE VIEW test_tablesample_v1 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10*2) REPEATABLE (2);
  CREATE VIEW test_tablesample_v2 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99);
  SELECT pg_get_viewdef('test_tablesample_v1'::regclass);
!                                  pg_get_viewdef
! --------------------------------------------------------------------------------
!   SELECT test_tablesample.id                                                   +
!     FROM test_tablesample TABLESAMPLE system (((10 * 2))::real) REPEATABLE (2);
  (1 row)

  SELECT pg_get_viewdef('test_tablesample_v2'::regclass);
!                       pg_get_viewdef
! -----------------------------------------------------------
!   SELECT test_tablesample.id                              +
!     FROM test_tablesample TABLESAMPLE system ((99)::real);
  (1 row)

  BEGIN;
! DECLARE tablesample_cur CURSOR FOR SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (100);
  FETCH FIRST FROM tablesample_cur;
   id
  ----
!   0
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   1
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   2
  (1 row)

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (10);
   id
  ----
-   0
-   1
-   2
    3
    4
    5
!   9
! (7 rows)

  FETCH NEXT FROM tablesample_cur;
   id
--- 1,102 ----
! CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10);
! -- use fillfactor so we don't have to load too much data to get multiple pages
! INSERT INTO test_tablesample
!   SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i);
! SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (0);
   id
  ----
    3
    4
    5
    6
    7
    8
! (6 rows)

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0/11) REPEATABLE (0);
!  id
! ----
! (0 rows)
!
! SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (0);
   count
  -------
      10
  (1 row)

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
   id
  ----
!   3
!   4
!   5
    6
    7
    8
! (6 rows)

! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (0);
   id
  ----
    4
    5
+   6
+   7
+   8
  (5 rows)

! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (0);
   id
  ----
!   7
! (1 row)

  CREATE VIEW test_tablesample_v1 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10*2) REPEATABLE (2);
  CREATE VIEW test_tablesample_v2 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99);
  SELECT pg_get_viewdef('test_tablesample_v1'::regclass);
!                              pg_get_viewdef
! ------------------------------------------------------------------------
!   SELECT test_tablesample.id                                           +
!     FROM test_tablesample TABLESAMPLE system ((10 * 2)) REPEATABLE (2);
  (1 row)

  SELECT pg_get_viewdef('test_tablesample_v2'::regclass);
!                   pg_get_viewdef
! ---------------------------------------------------
!   SELECT test_tablesample.id                      +
!     FROM test_tablesample TABLESAMPLE system (99);
  (1 row)

  BEGIN;
! DECLARE tablesample_cur CURSOR FOR SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
  FETCH FIRST FROM tablesample_cur;
   id
  ----
!   3
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   4
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   5
  (1 row)

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
   id
  ----
    3
    4
    5
!   6
!   7
!   8
! (6 rows)

  FETCH NEXT FROM tablesample_cur;
   id
*************** FETCH NEXT FROM tablesample_cur;
*** 124,142 ****
  FETCH FIRST FROM tablesample_cur;
   id
  ----
!   0
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   1
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   2
  (1 row)

  FETCH NEXT FROM tablesample_cur;
--- 119,137 ----
  FETCH FIRST FROM tablesample_cur;
   id
  ----
!   3
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   4
  (1 row)

  FETCH NEXT FROM tablesample_cur;
   id
  ----
!   5
  (1 row)

  FETCH NEXT FROM tablesample_cur;
*************** FETCH NEXT FROM tablesample_cur;
*** 159,199 ****

  CLOSE tablesample_cur;
  END;
! EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (10);
!                                   QUERY PLAN
! -------------------------------------------------------------------------------
!  Sample Scan (system) on test_tablesample  (cost=0.00..26.35 rows=635 width=4)
! (1 row)

  EXPLAIN SELECT * FROM test_tablesample_v1;
!                                   QUERY PLAN
! -------------------------------------------------------------------------------
!  Sample Scan (system) on test_tablesample  (cost=0.00..10.54 rows=254 width=4)
  (1 row)

  -- errors
  SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1);
! ERROR:  tablesample method "foobar" does not exist
  LINE 1: SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1);
!                        ^
  SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (NULL);
! ERROR:  REPEATABLE clause must be NOT NULL numeric value
! LINE 1: ... test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (NULL);
!                                                                  ^
  SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (-1);
  ERROR:  invalid sample size
! HINT:  Sample size must be numeric value between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (200);
  ERROR:  invalid sample size
! HINT:  Sample size must be numeric value between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (-1);
  ERROR:  invalid sample size
! HINT:  Sample size must be numeric value between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (200);
  ERROR:  invalid sample size
! HINT:  Sample size must be numeric value between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1);
! ERROR:  TABLESAMPLE clause can only be used on tables and materialized views
  LINE 1: SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1)...
                         ^
  INSERT INTO test_tablesample_v1 VALUES(1);
--- 154,253 ----

  CLOSE tablesample_cur;
  END;
! EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
!                               QUERY PLAN
! ----------------------------------------------------------------------
!  Sample Scan on test_tablesample  (cost=0.00..26.35 rows=635 width=4)
!    Sampling: system ('50'::real) REPEATABLE ('0'::double precision)
! (2 rows)

  EXPLAIN SELECT * FROM test_tablesample_v1;
!                               QUERY PLAN
! ----------------------------------------------------------------------
!  Sample Scan on test_tablesample  (cost=0.00..10.54 rows=254 width=4)
!    Sampling: system ('20'::real) REPEATABLE ('2'::double precision)
! (2 rows)
!
! -- check that collations get assigned within the tablesample arguments
! SELECT count(*) FROM test_tablesample TABLESAMPLE bernoulli (('1'::text < '0'::text)::int);
!  count
! -------
!      0
! (1 row)
!
! -- check behavior during rescans, as well as correct handling of min/max pct
! select * from
!   (values (0),(100)) v(pct),
!   lateral (select count(*) from tenk1 tablesample bernoulli (pct)) ss;
!  pct | count
! -----+-------
!    0 |     0
!  100 | 10000
! (2 rows)
!
! select * from
!   (values (0),(100)) v(pct),
!   lateral (select count(*) from tenk1 tablesample system (pct)) ss;
!  pct | count
! -----+-------
!    0 |     0
!  100 | 10000
! (2 rows)
!
! explain (costs off)
! select pct, count(unique1) from
!   (values (0),(100)) v(pct),
!   lateral (select * from tenk1 tablesample bernoulli (pct)) ss
!   group by pct;
!                        QUERY PLAN
! --------------------------------------------------------
!  HashAggregate
!    Group Key: "*VALUES*".column1
!    ->  Nested Loop
!          ->  Values Scan on "*VALUES*"
!          ->  Sample Scan on tenk1
!                Sampling: bernoulli ("*VALUES*".column1)
! (6 rows)
!
! select pct, count(unique1) from
!   (values (0),(100)) v(pct),
!   lateral (select * from tenk1 tablesample bernoulli (pct)) ss
!   group by pct;
!  pct | count
! -----+-------
!  100 | 10000
! (1 row)
!
! select pct, count(unique1) from
!   (values (0),(100)) v(pct),
!   lateral (select * from tenk1 tablesample system (pct)) ss
!   group by pct;
!  pct | count
! -----+-------
!  100 | 10000
  (1 row)

  -- errors
  SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1);
! ERROR:  tablesample method foobar does not exist
  LINE 1: SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1);
!                                                     ^
  SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (NULL);
! ERROR:  TABLESAMPLE REPEATABLE parameter cannot be null
  SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (-1);
  ERROR:  invalid sample size
! DETAIL:  Sample size must be between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (200);
  ERROR:  invalid sample size
! DETAIL:  Sample size must be between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (-1);
  ERROR:  invalid sample size
! DETAIL:  Sample size must be between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (200);
  ERROR:  invalid sample size
! DETAIL:  Sample size must be between 0 and 100 (inclusive).
  SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1);
! ERROR:  TABLESAMPLE clause can only be applied to tables and materialized views
  LINE 1: SELECT id FROM test_tablesample_v1 TABLESAMPLE BERNOULLI (1)...
                         ^
  INSERT INTO test_tablesample_v1 VALUES(1);
*************** DETAIL:  Views containing TABLESAMPLE ar
*** 202,229 ****
  HINT:  To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO
INSTEADrule. 
  WITH query_select AS (SELECT * FROM test_tablesample)
  SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1);
! ERROR:  TABLESAMPLE clause can only be used on tables and materialized views
  LINE 2: SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEA...
                        ^
  SELECT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPLE BERNOULLI (5);
  ERROR:  syntax error at or near "TABLESAMPLE"
  LINE 1: ...CT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPL...
                                                               ^
- -- catalog sanity
- SELECT *
- FROM pg_tablesample_method
- WHERE tsminit IS NULL
-    OR tsmseqscan IS NULL
-    OR tsmpagemode IS NULL
-    OR tsmnextblock IS NULL
-    OR tsmnexttuple IS NULL
-    OR tsmend IS NULL
-    OR tsmreset IS NULL
-    OR tsmcost IS NULL;
-  tsmname | tsmseqscan | tsmpagemode | tsminit | tsmnextblock | tsmnexttuple | tsmexaminetuple | tsmend | tsmreset |
tsmcost 
-
---------+------------+-------------+---------+--------------+--------------+-----------------+--------+----------+---------
- (0 rows)
-
  -- done
  DROP TABLE test_tablesample CASCADE;
  NOTICE:  drop cascades to 2 other objects
--- 256,268 ----
  HINT:  To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO
INSTEADrule. 
  WITH query_select AS (SELECT * FROM test_tablesample)
  SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1);
! ERROR:  TABLESAMPLE clause can only be applied to tables and materialized views
  LINE 2: SELECT * FROM query_select TABLESAMPLE BERNOULLI (5.5) REPEA...
                        ^
  SELECT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPLE BERNOULLI (5);
  ERROR:  syntax error at or near "TABLESAMPLE"
  LINE 1: ...CT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPL...
                                                               ^
  -- done
  DROP TABLE test_tablesample CASCADE;
  NOTICE:  drop cascades to 2 other objects
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3a607cf..15d74d4 100644
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: lock
*** 110,115 ****
--- 110,116 ----
  test: replica_identity
  test: rowsecurity
  test: object_address
+ test: tablesample
  test: alter_generic
  test: alter_operator
  test: misc
*************** test: with
*** 156,159 ****
  test: xml
  test: event_trigger
  test: stats
- test: tablesample
--- 157,159 ----
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 782824a..2495f32 100644
*** a/src/test/regress/sql/rowsecurity.sql
--- b/src/test/regress/sql/rowsecurity.sql
*************** SET row_security TO ON;
*** 94,107 ****
  SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
  SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;

! SELECT * FROM document TABLESAMPLE BERNOULLI (50) REPEATABLE(1) WHERE f_leak(dtitle) ORDER BY did;

  -- viewpoint from rls_regress_user2
  SET SESSION AUTHORIZATION rls_regress_user2;
  SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
  SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;

! SELECT * FROM document TABLESAMPLE BERNOULLI (50) REPEATABLE(1) WHERE f_leak(dtitle) ORDER BY did;

  EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
  EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
--- 94,111 ----
  SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
  SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;

! -- try a sampled version
! SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
!   WHERE f_leak(dtitle) ORDER BY did;

  -- viewpoint from rls_regress_user2
  SET SESSION AUTHORIZATION rls_regress_user2;
  SELECT * FROM document WHERE f_leak(dtitle) ORDER BY did;
  SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle) ORDER BY did;

! -- try a sampled version
! SELECT * FROM document TABLESAMPLE BERNOULLI(50) REPEATABLE(0)
!   WHERE f_leak(dtitle) ORDER BY did;

  EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);
  EXPLAIN (COSTS OFF) SELECT * FROM document NATURAL JOIN category WHERE f_leak(dtitle);
diff --git a/src/test/regress/sql/tablesample.sql b/src/test/regress/sql/tablesample.sql
index 7b3eb9b..e4b5636 100644
*** a/src/test/regress/sql/tablesample.sql
--- b/src/test/regress/sql/tablesample.sql
***************
*** 1,13 ****
! CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- force smaller pages so we don't have to
loadtoo much data to get multiple pages 

! INSERT INTO test_tablesample SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i) ORDER BY i;

! SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (10);
! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0/11) REPEATABLE (9999);
! SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100);
! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (100);
! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (100);
! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (1);

  CREATE VIEW test_tablesample_v1 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10*2) REPEATABLE (2);
  CREATE VIEW test_tablesample_v2 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99);
--- 1,15 ----
! CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10);
! -- use fillfactor so we don't have to load too much data to get multiple pages

! INSERT INTO test_tablesample
!   SELECT i, repeat(i::text, 200) FROM generate_series(0, 9) s(i);

! SELECT t.id FROM test_tablesample AS t TABLESAMPLE SYSTEM (50) REPEATABLE (0);
! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (100.0/11) REPEATABLE (0);
! SELECT count(*) FROM test_tablesample TABLESAMPLE SYSTEM (100) REPEATABLE (0);
! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (50) REPEATABLE (0);
! SELECT id FROM test_tablesample TABLESAMPLE BERNOULLI (5.5) REPEATABLE (0);

  CREATE VIEW test_tablesample_v1 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (10*2) REPEATABLE (2);
  CREATE VIEW test_tablesample_v2 AS SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (99);
*************** SELECT pg_get_viewdef('test_tablesample_
*** 15,26 ****
  SELECT pg_get_viewdef('test_tablesample_v2'::regclass);

  BEGIN;
! DECLARE tablesample_cur CURSOR FOR SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (100);
  FETCH FIRST FROM tablesample_cur;
  FETCH NEXT FROM tablesample_cur;
  FETCH NEXT FROM tablesample_cur;

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (10);

  FETCH NEXT FROM tablesample_cur;
  FETCH NEXT FROM tablesample_cur;
--- 17,28 ----
  SELECT pg_get_viewdef('test_tablesample_v2'::regclass);

  BEGIN;
! DECLARE tablesample_cur CURSOR FOR SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
  FETCH FIRST FROM tablesample_cur;
  FETCH NEXT FROM tablesample_cur;
  FETCH NEXT FROM tablesample_cur;

! SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);

  FETCH NEXT FROM tablesample_cur;
  FETCH NEXT FROM tablesample_cur;
*************** FETCH NEXT FROM tablesample_cur;
*** 36,44 ****
  CLOSE tablesample_cur;
  END;

! EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (10);
  EXPLAIN SELECT * FROM test_tablesample_v1;

  -- errors
  SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1);

--- 38,70 ----
  CLOSE tablesample_cur;
  END;

! EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE SYSTEM (50) REPEATABLE (0);
  EXPLAIN SELECT * FROM test_tablesample_v1;

+ -- check that collations get assigned within the tablesample arguments
+ SELECT count(*) FROM test_tablesample TABLESAMPLE bernoulli (('1'::text < '0'::text)::int);
+
+ -- check behavior during rescans, as well as correct handling of min/max pct
+ select * from
+   (values (0),(100)) v(pct),
+   lateral (select count(*) from tenk1 tablesample bernoulli (pct)) ss;
+ select * from
+   (values (0),(100)) v(pct),
+   lateral (select count(*) from tenk1 tablesample system (pct)) ss;
+ explain (costs off)
+ select pct, count(unique1) from
+   (values (0),(100)) v(pct),
+   lateral (select * from tenk1 tablesample bernoulli (pct)) ss
+   group by pct;
+ select pct, count(unique1) from
+   (values (0),(100)) v(pct),
+   lateral (select * from tenk1 tablesample bernoulli (pct)) ss
+   group by pct;
+ select pct, count(unique1) from
+   (values (0),(100)) v(pct),
+   lateral (select * from tenk1 tablesample system (pct)) ss
+   group by pct;
+
  -- errors
  SELECT id FROM test_tablesample TABLESAMPLE FOOBAR (1);

*************** SELECT * FROM query_select TABLESAMPLE B
*** 57,74 ****

  SELECT q.* FROM (SELECT * FROM test_tablesample) as q TABLESAMPLE BERNOULLI (5);

- -- catalog sanity
-
- SELECT *
- FROM pg_tablesample_method
- WHERE tsminit IS NULL
-    OR tsmseqscan IS NULL
-    OR tsmpagemode IS NULL
-    OR tsmnextblock IS NULL
-    OR tsmnexttuple IS NULL
-    OR tsmend IS NULL
-    OR tsmreset IS NULL
-    OR tsmcost IS NULL;
-
  -- done
  DROP TABLE test_tablesample CASCADE;
--- 83,87 ----

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Fabrízio de Royes Mello
Дата:
Сообщение: Re: Doubt about AccessExclusiveLock in ALTER TABLE .. SET ( .. );
Следующее
От: Tatsuo Ishii
Дата:
Сообщение: Re: BRIN index and aborted transaction