tablesample_context.patch

application/octet-stream

Filename: tablesample_context.patch
Type: application/octet-stream
Part: 0
Message: [PATCH]Tablesample Submission

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/access/heap/heapam.c 303 0
src/backend/commands/analyze.c 5 11
src/backend/commands/explain.c 6 0
src/backend/executor/execAmi.c 18 0
src/backend/executor/execCurrent.c 1 0
src/backend/executor/execProcnode.c 14 0
src/backend/executor/Makefile 1 0
src/backend/executor/nodeSamplescan.c 345 0
src/backend/nodes/copyfuncs.c 44 0
src/backend/nodes/equalfuncs.c 16 0
src/backend/nodes/outfuncs.c 30 0
src/backend/nodes/readfuncs.c 17 0
src/backend/optimizer/path/allpaths.c 20 0
src/backend/optimizer/path/costsize.c 57 0
src/backend/optimizer/plan/createplan.c 81 0
src/backend/optimizer/plan/planner.c 3 0
src/backend/optimizer/plan/setrefs.c 11 0
src/backend/optimizer/plan/subselect.c 4 0
src/backend/optimizer/util/pathnode.c 24 0
src/backend/optimizer/util/relnode.c 2 0
src/backend/parser/analyze.c 9 0
src/backend/parser/gram.y 74 0
src/backend/parser/parse_clause.c 6 0
src/backend/parser/parse_relation.c 8 0
src/backend/utils/adt/ruleutils.c 26 0
src/backend/utils/errcodes.txt 1 0
src/backend/utils/misc/Makefile 1 0
src/backend/utils/misc/sampleutils.c 272 0
src/include/access/relscan.h 13 0
src/include/commands/vacuum.h 0 0
src/include/executor/nodeSamplescan.h 28 0
src/include/nodes/execnodes.h 20 0
src/include/nodes/nodes.h 3 0
src/include/nodes/parsenodes.h 4 0
src/include/nodes/plannodes.h 18 0
src/include/nodes/primnodes.h 26 0
src/include/nodes/relation.h 11 0
src/include/optimizer/cost.h 2 0
src/include/optimizer/pathnode.h 1 0
src/include/parser/kwlist.h 2 0
src/include/utils/sampleutils.h 49 0
src/test/regress/expected/tablesample.out 188 0
src/test/regress/parallel_schedule 1 0
src/test/regress/serial_schedule 1 0
src/test/regress/sql/tablesample.sql 58 0
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 69,74 ****
--- 69,75 ----
  #include "utils/snapmgr.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
+ #include "utils/sampleutils.h"
  
  
  /* GUC variable */
***************
*** 87,93 **** static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
  				bool all_visible_cleared, bool new_all_visible_cleared);
  static bool HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
  					   HeapTuple oldtup, HeapTuple newtup);
! 
  
  /* ----------------------------------------------------------------
   *						 heap support routines
--- 88,96 ----
  				bool all_visible_cleared, bool new_all_visible_cleared);
  static bool HeapSatisfiesHOTUpdate(Relation relation, Bitmapset *hot_attrs,
  					   HeapTuple oldtup, HeapTuple newtup);
! static void acquire_next_sampletup(HeapScanDesc scan);
! static void acquire_block_sample(HeapScanDesc scan);
! static void	heapnullreturn(HeapScanDesc scan);
  
  /* ----------------------------------------------------------------
   *						 heap support routines
***************
*** 588,593 **** heapgettup(HeapScanDesc scan,
--- 591,599 ----
   *
   *		Same API as heapgettup, but used in page-at-a-time mode
   *
+  * SampleScan System algo is also implemented. Direction of Backward is especially used
+  * under CURSOR query.
+  *
   * The internal logic is much the same as heapgettup's too, but there are some
   * differences: we do not take the buffer content lock (that only needs to
   * happen inside heapgetpage), and we iterate through just the tuples listed
***************
*** 612,617 **** heapgettup_pagemode(HeapScanDesc scan,
--- 618,626 ----
  	OffsetNumber lineoff;
  	int			linesleft;
  	ItemId		lpp;
+ 	int			rand_percent;
+ 	int			sample_percent = scan->sample_percent;
+ 	void		*rand_state = scan->rs_randstate; /* random generator state */
  
  	/*
  	 * calculate next starting lineindex, given scan direction
***************
*** 630,635 **** heapgettup_pagemode(HeapScanDesc scan,
--- 639,664 ----
  				return;
  			}
  			page = scan->rs_startblock; /* first page */
+ 
+ 			/* SampleScan, implement SYSTEM page selection */
+ 			if(scan->is_sample_scan)
+ 			{
+ 				while(page < scan->rs_nblocks)
+ 				{
+ 					rand_percent = get_rand_in_range(rand_state, 0, 100);
+ 					if(rand_percent >= sample_percent)
+ 						page++;
+ 					else break;
+ 				}
+ 				finished = (page >= scan->rs_nblocks);
+ 				/* All pages just skiped, clear up and return */
+ 				if(finished)
+ 				{
+ 					heapnullreturn(scan);
+ 					return;
+ 				}
+ 			}
+ 
  			heapgetpage(scan, page);
  			lineindex = 0;
  			scan->rs_inited = true;
***************
*** 673,678 **** heapgettup_pagemode(HeapScanDesc scan,
--- 702,727 ----
  				page = scan->rs_startblock - 1;
  			else
  				page = scan->rs_nblocks - 1;
+ 
+ 			/* SampleScan, implement SYSTEM page selection */
+ 			if(scan->is_sample_scan)
+ 			{
+ 				while(page > 0)
+ 				{
+ 					rand_percent = get_rand_in_range(rand_state, 0, 100);
+ 					if(rand_percent >= sample_percent)
+ 						page--;
+ 					else break;
+ 				}
+ 				finished = (page == scan->rs_startblock);
+ 				/* All pages just skiped, clear up and return */
+ 				if(finished)
+ 				{
+ 					heapnullreturn(scan);
+ 					return;
+ 				}
+ 			}
+ 
  			heapgetpage(scan, page);
  		}
  		else
***************
*** 701,706 **** heapgettup_pagemode(HeapScanDesc scan,
--- 750,756 ----
  	{
  		/*
  		 * ``no movement'' scan direction: refetch prior tuple
+ 		 * Same behavior for SampleScan, so no change
  		 */
  		if (!scan->rs_inited)
  		{
***************
*** 782,797 **** heapgettup_pagemode(HeapScanDesc scan,
  		 */
  		if (backward)
  		{
  			finished = (page == scan->rs_startblock);
! 			if (page == 0)
  				page = scan->rs_nblocks;
  			page--;
  		}
  		else
  		{
  			page++;
  			if (page >= scan->rs_nblocks)
! 				page = 0;
  			finished = (page == scan->rs_startblock);
  
  			/*
--- 832,878 ----
  		 */
  		if (backward)
  		{
+ 			/* SampleScan, implement SYSTEM page selection */
+ 			if(scan->is_sample_scan)
+ 			{
+ 				while(page > scan->rs_startblock)
+ 				{
+ 					rand_percent = get_rand_in_range(rand_state, 0, 100);
+ 					if(rand_percent >= sample_percent)
+ 						page--;
+ 					else break;
+ 				}
+ 			}
+ 
  			finished = (page == scan->rs_startblock);
! 			/*
! 			 * It was deciding page == 0, now change to 
! 			 * page == scan->rs_startblock, otherwise doesn't make sense.
! 			 * Is this reasoning correct?
! 			 */
! 			if (page == scan->rs_startblock)
  				page = scan->rs_nblocks;
  			page--;
  		}
  		else
  		{
  			page++;
+ 
+ 			/* SampleScan, implement SYSTEM page selection */
+ 			while(page < scan->rs_nblocks)
+ 			{
+ 				rand_percent = get_rand_in_range(rand_state, 0, 100);
+ 				if(rand_percent >= sample_percent)
+ 					page++;
+ 				else break;
+ 			}
+ 			/*
+ 			 * It was setting page = 0, now change to 
+ 			 * page = scan->rs_startblock, otherwise doesn't make sense.
+ 			 * Is this reasoning correct?
+ 			 */
  			if (page >= scan->rs_nblocks)
! 				page = scan->rs_startblock;
  			finished = (page == scan->rs_startblock);
  
  			/*
***************
*** 815,826 **** heapgettup_pagemode(HeapScanDesc 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;
  		}
  
--- 896,902 ----
  		 */
  		if (finished)
  		{
! 			heapnullreturn(scan);
  			return;
  		}
  
***************
*** 836,841 **** heapgettup_pagemode(HeapScanDesc scan,
--- 912,930 ----
  	}
  }
  
+ static void
+ heapnullreturn(HeapScanDesc scan)
+ {
+ 	HeapTuple	tuple = &(scan->rs_ctup);
+ 
+ 	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;
+ }
  
  #if defined(DISABLE_COMPLEX_MACRO)
  /*
***************
*** 1236,1241 **** heap_beginscan_internal(Relation relation, Snapshot snapshot,
--- 1325,1333 ----
  
  	initscan(scan, key, false);
  
+ 	/* By default, it is not samplescan */
+ 	scan->is_sample_scan = false;
+ 
  	return scan;
  }
  
***************
*** 1315,1332 **** heap_endscan(HeapScanDesc scan)
  #endif   /* !defined(HEAPDEBUGALL) */
  
  
  HeapTuple
  heap_getnext(HeapScanDesc scan, ScanDirection direction)
  {
  	/* Note: no locking manipulations needed */
  
  	HEAPDEBUG_1;				/* heap_getnext( info ) */
  
! 	if (scan->rs_pageatatime)
! 		heapgettup_pagemode(scan, direction,
! 							scan->rs_nkeys, scan->rs_key);
! 	else
! 		heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
  
  	if (scan->rs_ctup.t_data == NULL)
  	{
--- 1407,1446 ----
  #endif   /* !defined(HEAPDEBUGALL) */
  
  
+ /*
+  * SampleScan is implemented from this function.
+  */
  HeapTuple
  heap_getnext(HeapScanDesc scan, ScanDirection direction)
  {
  	/* Note: no locking manipulations needed */
  
+ 	TableSampleMethod sample_method = scan->sample_method;
+ 
  	HEAPDEBUG_1;				/* heap_getnext( info ) */
  
! 	/* if sample percent is 0, just return NULL */
! 	if (scan->is_sample_scan && scan->sample_percent == 0)
! 		return NULL;
! 
! 	/* SampleScan or SeqScan */
! 	if (scan->is_sample_scan)
! 	{
! 		/* Which sample method */
! 		if(sample_method == SAMPLE_SYSTEM)
! 			/* This function will check scan type inside */
! 			heapgettup_pagemode(scan, direction,
! 								scan->rs_nkeys, scan->rs_key);
! 		else if(sample_method == SAMPLE_BERNOULLI)
! 			acquire_next_sampletup(scan);
! 	} else 
! 	{
! 		if (scan->rs_pageatatime)
! 			heapgettup_pagemode(scan, direction,
! 								scan->rs_nkeys, scan->rs_key);
! 		else
! 			heapgettup(scan, direction, scan->rs_nkeys, scan->rs_key);
! 	}
  
  	if (scan->rs_ctup.t_data == NULL)
  	{
***************
*** 5780,5782 **** heap_sync(Relation rel)
--- 5894,6071 ----
  		heap_close(toastrel, AccessShareLock);
  	}
  }
+ 
+ /*
+  * acquire_next_sampletup -- algo modified from acquire_sample_rows in analyze.c.
+  * The core algo is inside acquire_block_sample.
+  */
+ static void
+ acquire_next_sampletup(HeapScanDesc scan)
+ {
+ 	HeapTuple		tuple = &(scan->rs_ctup);
+ 	HeapTuple		pass_tuple;
+ 
+ 	/* Collect the sample first if not yet */
+ 	if(!scan->rs_sampleinited)
+ 		acquire_block_sample(scan);
+ 
+ 	/* Just retrieve tuple one by one from the sample collection */
+ 	if(scan->rs_curindex < scan->rs_samplesize)
+ 	{
+ 		/* Get the head tuple from sampled tuple array */
+ 		pass_tuple = scan->rs_samplerows[scan->rs_curindex];
+ 		tuple->t_len = pass_tuple->t_len;
+ 		tuple->t_self = pass_tuple->t_self;
+ 		tuple->t_tableOid = pass_tuple->t_tableOid;
+ 		tuple->t_data = pass_tuple->t_data;
+ 		scan->rs_curindex++;
+ 	}
+ 	else
+ 	{
+ 		/* No more tuple to return */
+ 		scan->rs_cbuf = InvalidBuffer;
+ 		scan->rs_cblock = InvalidBlockNumber;
+ 		tuple->t_data = NULL;
+ 	}
+ 
+ 	return;
+ }
+ 
+ /*
+  * See acquire_sample_rows in analyze.c for detail description of the algo.
+  * Selected rows are returned in the caller-allocated array scan->rs_samplerows, which
+  * must have at least targrows entries.
+  *
+  */
+ static void
+ acquire_block_sample(HeapScanDesc scan)
+ {
+ 	int			numrows = 0;	/* # rows now in reservoir */
+ 	double		samplerows = 0; /* total # rows collected */
+ 	double		rowstoskip = -1;	/* -1 means not set yet */
+ 	int			targrows = scan->targrows;
+ 	HeapTuple	*rows = scan->rs_samplerows;
+ 	BlockNumber	totalblocks = scan->rs_nblocks;
+ 	BlockSamplerData bs;
+ 	Relation onerel = scan->rs_rd;
+ 	void	*rand_state = scan->rs_randstate;
+ 	double		rstate;
+ 
+ 	/* If the required sample size is 0, we just return */
+ 	if(targrows == 0)
+ 	{
+ 		scan->rs_samplesize = 0;
+ 		scan->rs_sampleinited = true;
+ 		return;
+ 	}
+ 
+ 	Assert(targrows > 0);
+ 
+ 	/* Prepare for sampling block numbers */
+ 	BlockSampler_Init(&bs, totalblocks, targrows);
+ 	/* Prepare for sampling rows */
+ 	rstate = anl_init_selection_state(rand_state, targrows);
+ 
+ 	/* Outer loop over blocks to sample */
+ 	while (BlockSampler_HasMore(&bs))
+ 	{
+ 		BlockNumber targblock = BlockSampler_Next(rand_state, &bs);
+ 		Buffer		targbuffer;
+ 		Page		targpage;
+ 		OffsetNumber targoffset,
+ 					maxoffset;
+ 
+ 		/*
+ 		 * We must maintain a pin on the target page's buffer to ensure that
+ 		 * the maxoffset value stays good (else concurrent VACUUM might delete
+ 		 * tuples out from under us).  Hence, pin the page until we are done
+ 		 * looking at it.  We also choose to hold sharelock on the buffer
+ 		 * throughout --- we could release and re-acquire sharelock for each
+ 		 * tuple, but since we aren't doing much work per tuple, the extra
+ 		 * lock traffic is probably better avoided.
+ 		 */
+ 		targbuffer = ReadBufferExtended(onerel, MAIN_FORKNUM, targblock,
+ 										RBM_NORMAL, scan->rs_strategy);
+ 		LockBuffer(targbuffer, BUFFER_LOCK_SHARE);
+ 		targpage = BufferGetPage(targbuffer);
+ 		maxoffset = PageGetMaxOffsetNumber(targpage);
+ 
+ 		/* Inner loop over all tuples on the selected page */
+ 		for (targoffset = FirstOffsetNumber; targoffset <= maxoffset; targoffset++)
+ 		{
+ 			ItemId		itemid;
+ 			HeapTupleData targtuple;
+ 
+ 			itemid = PageGetItemId(targpage, targoffset);
+ 
+ 			/*
+ 			 * We ignore unused and redirect line pointers.  DEAD line
+ 			 * pointers should be counted as dead, because we need vacuum to
+ 			 * run to get rid of them.	Note that this rule agrees with the
+ 			 * way that heap_page_prune() counts things.
+ 			 */
+ 			if (!ItemIdIsNormal(itemid))
+ 			{
+ 				continue;
+ 			}
+ 
+ 			ItemPointerSet(&targtuple.t_self, targblock, targoffset);
+ 
+ 			targtuple.t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
+ 			targtuple.t_len = ItemIdGetLength(itemid);
+ 
+ 			/*
+ 			 * The first targrows sample rows are simply copied into the
+ 			 * reservoir. Then we start replacing tuples in the sample
+ 			 * until we reach the end of the relation.	This algorithm is
+ 			 * from Jeff Vitter's paper (see full citation below). It
+ 			 * works by repeatedly computing the number of tuples to skip
+ 			 * before selecting a tuple, which replaces a randomly chosen
+ 			 * element of the reservoir (current set of tuples).  At all
+ 			 * times the reservoir is a true random sample of the tuples
+ 			 * we've passed over so far, so when we fall off the end of
+ 			 * the relation we're done.
+ 			 */
+ 			if (numrows < targrows)
+ 				rows[numrows++] = heap_copytuple(&targtuple);
+ 			else
+ 			{
+ 				/*
+ 				 * t in Vitter's paper is the number of records already
+ 				 * processed.  If we need to compute a new S value, we
+ 				 * must use the not-yet-incremented value of samplerows as
+ 				 * t.
+ 				 */
+ 				if (rowstoskip < 0)
+ 					rowstoskip = anl_get_next_S(rand_state, samplerows, targrows,
+ 												&rstate);
+ 
+ 				if (rowstoskip <= 0)
+ 				{
+ 					/*
+ 					 * Found a suitable tuple, so save it, replacing one
+ 					 * old tuple at random
+ 					 */
+ 					int			k = (int) (targrows * anl_random_fract(rand_state));
+ 
+ 					Assert(k >= 0 && k < targrows);
+ 					heap_freetuple(rows[k]);
+ 					rows[k] = heap_copytuple(&targtuple);
+ 				}
+ 
+ 				rowstoskip -= 1;
+ 			}
+ 
+ 			samplerows += 1;
+ 		}
+ 
+ 		/* Now release the lock and pin on the page */
+ 		UnlockReleaseBuffer(targbuffer);
+ 	}
+ 
+ 	scan->rs_samplesize = numrows;
+ 	scan->rs_sampleinited = true;
+ 
+ 	return;
+ }
+ 
*** a/src/backend/commands/analyze.c
--- b/src/backend/commands/analyze.c
***************
*** 52,70 ****
  #include "utils/syscache.h"
  #include "utils/timestamp.h"
  #include "utils/tqual.h"
  
  
- /* Data structure for Algorithm S from Knuth 3.4.2 */
- typedef struct
- {
- 	BlockNumber N;				/* number of blocks, known in advance */
- 	int			n;				/* desired sample size */
- 	BlockNumber t;				/* current block number */
- 	int			m;				/* blocks selected so far */
- } BlockSamplerData;
- 
- typedef BlockSamplerData *BlockSampler;
- 
  /* Per-index data for ANALYZE */
  typedef struct AnlIndexData
  {
--- 52,60 ----
  #include "utils/syscache.h"
  #include "utils/timestamp.h"
  #include "utils/tqual.h"
+ #include "utils/sampleutils.h"
  
  
  /* Per-index data for ANALYZE */
  typedef struct AnlIndexData
  {
***************
*** 86,95 **** static BufferAccessStrategy vac_strategy;
  static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
  			   AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
  			   bool inh, int elevel);
- static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
- 				  int samplesize);
- static bool BlockSampler_HasMore(BlockSampler bs);
- static BlockNumber BlockSampler_Next(BlockSampler bs);
  static void compute_index_stats(Relation onerel, double totalrows,
  					AnlIndexData *indexdata, int nindexes,
  					HeapTuple *rows, int numrows,
--- 76,81 ----
***************
*** 108,114 **** static void update_attstats(Oid relid, bool inh,
  static Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
  static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
  
- 
  /*
   *	analyze_rel() -- analyze one relation
   */
--- 94,99 ----
***************
*** 937,1030 **** examine_attribute(Relation onerel, int attnum, Node *index_expr)
  }
  
  /*
-  * BlockSampler_Init -- prepare for random sampling of blocknumbers
-  *
-  * BlockSampler is used for stage one of our new two-stage tuple
-  * sampling mechanism as discussed on pgsql-hackers 2004-04-02 (subject
-  * "Large DB").  It selects a random sample of samplesize blocks out of
-  * the nblocks blocks in the table.  If the table has less than
-  * samplesize blocks, all blocks are selected.
-  *
-  * Since we know the total number of blocks in advance, we can use the
-  * straightforward Algorithm S from Knuth 3.4.2, rather than Vitter's
-  * algorithm.
-  */
- static void
- BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize)
- {
- 	bs->N = nblocks;			/* measured table size */
- 
- 	/*
- 	 * If we decide to reduce samplesize for tables that have less or not much
- 	 * more than samplesize blocks, here is the place to do it.
- 	 */
- 	bs->n = samplesize;
- 	bs->t = 0;					/* blocks scanned so far */
- 	bs->m = 0;					/* blocks selected so far */
- }
- 
- static bool
- BlockSampler_HasMore(BlockSampler bs)
- {
- 	return (bs->t < bs->N) && (bs->m < bs->n);
- }
- 
- static BlockNumber
- BlockSampler_Next(BlockSampler bs)
- {
- 	BlockNumber K = bs->N - bs->t;		/* remaining blocks */
- 	int			k = bs->n - bs->m;		/* blocks still to sample */
- 	double		p;				/* probability to skip block */
- 	double		V;				/* random */
- 
- 	Assert(BlockSampler_HasMore(bs));	/* hence K > 0 and k > 0 */
- 
- 	if ((BlockNumber) k >= K)
- 	{
- 		/* need all the rest */
- 		bs->m++;
- 		return bs->t++;
- 	}
- 
- 	/*----------
- 	 * It is not obvious that this code matches Knuth's Algorithm S.
- 	 * Knuth says to skip the current block with probability 1 - k/K.
- 	 * If we are to skip, we should advance t (hence decrease K), and
- 	 * repeat the same probabilistic test for the next block.  The naive
- 	 * implementation thus requires an anl_random_fract() call for each block
- 	 * number.	But we can reduce this to one anl_random_fract() call per
- 	 * selected block, by noting that each time the while-test succeeds,
- 	 * we can reinterpret V as a uniform random number in the range 0 to p.
- 	 * Therefore, instead of choosing a new V, we just adjust p to be
- 	 * the appropriate fraction of its former value, and our next loop
- 	 * makes the appropriate probabilistic test.
- 	 *
- 	 * We have initially K > k > 0.  If the loop reduces K to equal k,
- 	 * the next while-test must fail since p will become exactly zero
- 	 * (we assume there will not be roundoff error in the division).
- 	 * (Note: Knuth suggests a "<=" loop condition, but we use "<" just
- 	 * to be doubly sure about roundoff error.)  Therefore K cannot become
- 	 * less than k, which means that we cannot fail to select enough blocks.
- 	 *----------
- 	 */
- 	V = anl_random_fract();
- 	p = 1.0 - (double) k / (double) K;
- 	while (V < p)
- 	{
- 		/* skip */
- 		bs->t++;
- 		K--;					/* keep K == N - t */
- 
- 		/* adjust p to be new cutoff point in reduced range */
- 		p *= 1.0 - (double) k / (double) K;
- 	}
- 
- 	/* select */
- 	bs->m++;
- 	return bs->t++;
- }
- 
- /*
   * acquire_sample_rows -- acquire a random sample of rows from the table
   *
   * Selected rows are returned in the caller-allocated array rows[], which
--- 922,927 ----
***************
*** 1082,1093 **** acquire_sample_rows(Relation onerel, int elevel,
  	/* Prepare for sampling block numbers */
  	BlockSampler_Init(&bs, totalblocks, targrows);
  	/* Prepare for sampling rows */
! 	rstate = anl_init_selection_state(targrows);
  
  	/* Outer loop over blocks to sample */
  	while (BlockSampler_HasMore(&bs))
  	{
! 		BlockNumber targblock = BlockSampler_Next(&bs);
  		Buffer		targbuffer;
  		Page		targpage;
  		OffsetNumber targoffset,
--- 979,990 ----
  	/* Prepare for sampling block numbers */
  	BlockSampler_Init(&bs, totalblocks, targrows);
  	/* Prepare for sampling rows */
! 	rstate = anl_init_selection_state(NULL, targrows);
  
  	/* Outer loop over blocks to sample */
  	while (BlockSampler_HasMore(&bs))
  	{
! 		BlockNumber targblock = BlockSampler_Next(NULL, &bs);
  		Buffer		targbuffer;
  		Page		targpage;
  		OffsetNumber targoffset,
***************
*** 1229,1235 **** acquire_sample_rows(Relation onerel, int elevel,
  					 * t.
  					 */
  					if (rowstoskip < 0)
! 						rowstoskip = anl_get_next_S(samplerows, targrows,
  													&rstate);
  
  					if (rowstoskip <= 0)
--- 1126,1132 ----
  					 * t.
  					 */
  					if (rowstoskip < 0)
! 						rowstoskip = anl_get_next_S(NULL, samplerows, targrows,
  													&rstate);
  
  					if (rowstoskip <= 0)
***************
*** 1238,1244 **** acquire_sample_rows(Relation onerel, int elevel,
  						 * Found a suitable tuple, so save it, replacing one
  						 * old tuple at random
  						 */
! 						int			k = (int) (targrows * anl_random_fract());
  
  						Assert(k >= 0 && k < targrows);
  						heap_freetuple(rows[k]);
--- 1135,1141 ----
  						 * Found a suitable tuple, so save it, replacing one
  						 * old tuple at random
  						 */
! 						int			k = (int) (targrows * anl_random_fract(NULL));
  
  						Assert(k >= 0 && k < targrows);
  						heap_freetuple(rows[k]);
***************
*** 1297,1412 **** acquire_sample_rows(Relation onerel, int elevel,
  	return numrows;
  }
  
- /* Select a random value R uniformly distributed in (0 - 1) */
- double
- anl_random_fract(void)
- {
- 	return ((double) random() + 1) / ((double) MAX_RANDOM_VALUE + 2);
- }
- 
- /*
-  * These two routines embody Algorithm Z from "Random sampling with a
-  * reservoir" by Jeffrey S. Vitter, in ACM Trans. Math. Softw. 11, 1
-  * (Mar. 1985), Pages 37-57.  Vitter describes his algorithm in terms
-  * of the count S of records to skip before processing another record.
-  * It is computed primarily based on t, the number of records already read.
-  * The only extra state needed between calls is W, a random state variable.
-  *
-  * anl_init_selection_state computes the initial W value.
-  *
-  * Given that we've already read t records (t >= n), anl_get_next_S
-  * determines the number of records to skip before the next record is
-  * processed.
-  */
- double
- anl_init_selection_state(int n)
- {
- 	/* Initial value of W (for use when Algorithm Z is first applied) */
- 	return exp(-log(anl_random_fract()) / n);
- }
- 
- double
- anl_get_next_S(double t, int n, double *stateptr)
- {
- 	double		S;
- 
- 	/* The magic constant here is T from Vitter's paper */
- 	if (t <= (22.0 * n))
- 	{
- 		/* Process records using Algorithm X until t is large enough */
- 		double		V,
- 					quot;
- 
- 		V = anl_random_fract(); /* Generate V */
- 		S = 0;
- 		t += 1;
- 		/* Note: "num" in Vitter's code is always equal to t - n */
- 		quot = (t - (double) n) / t;
- 		/* Find min S satisfying (4.1) */
- 		while (quot > V)
- 		{
- 			S += 1;
- 			t += 1;
- 			quot *= (t - (double) n) / t;
- 		}
- 	}
- 	else
- 	{
- 		/* Now apply Algorithm Z */
- 		double		W = *stateptr;
- 		double		term = t - (double) n + 1;
- 
- 		for (;;)
- 		{
- 			double		numer,
- 						numer_lim,
- 						denom;
- 			double		U,
- 						X,
- 						lhs,
- 						rhs,
- 						y,
- 						tmp;
- 
- 			/* Generate U and X */
- 			U = anl_random_fract();
- 			X = t * (W - 1.0);
- 			S = floor(X);		/* S is tentatively set to floor(X) */
- 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
- 			tmp = (t + 1) / term;
- 			lhs = exp(log(((U * tmp * tmp) * (term + S)) / (t + X)) / n);
- 			rhs = (((t + X) / (term + S)) * term) / t;
- 			if (lhs <= rhs)
- 			{
- 				W = rhs / lhs;
- 				break;
- 			}
- 			/* Test if U <= f(S)/cg(X) */
- 			y = (((U * (t + 1)) / term) * (t + S + 1)) / (t + X);
- 			if ((double) n < S)
- 			{
- 				denom = t;
- 				numer_lim = term + S;
- 			}
- 			else
- 			{
- 				denom = t - (double) n + S;
- 				numer_lim = t + 1;
- 			}
- 			for (numer = t + S; numer >= numer_lim; numer -= 1)
- 			{
- 				y *= numer / denom;
- 				denom -= 1;
- 			}
- 			W = exp(-log(anl_random_fract()) / n);		/* Generate W in advance */
- 			if (exp(log(y) / n) <= (t + X) / t)
- 				break;
- 		}
- 		*stateptr = W;
- 	}
- 	return S;
- }
- 
  /*
   * qsort comparator for sorting rows[] array
   */
--- 1194,1199 ----
***************
*** 1431,1437 **** compare_rows(const void *a, const void *b)
  	return 0;
  }
  
- 
  /*
   * acquire_inherited_sample_rows -- acquire sample rows from inheritance tree
   *
--- 1218,1223 ----
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
***************
*** 725,730 **** ExplainNode(PlanState *planstate, List *ancestors,
--- 725,733 ----
  		case T_SeqScan:
  			pname = sname = "Seq Scan";
  			break;
+ 		case T_SampleScan:
+ 			pname = sname = "Sample Scan";
+ 			break;
  		case T_IndexScan:
  			pname = sname = "Index Scan";
  			break;
***************
*** 864,869 **** ExplainNode(PlanState *planstate, List *ancestors,
--- 867,873 ----
  	switch (nodeTag(plan))
  	{
  		case T_SeqScan:
+ 		case T_SampleScan:
  		case T_BitmapHeapScan:
  		case T_TidScan:
  		case T_SubqueryScan:
***************
*** 1109,1114 **** ExplainNode(PlanState *planstate, List *ancestors,
--- 1113,1119 ----
  										   planstate, es);
  			/* FALL THRU */
  		case T_SeqScan:
+ 		case T_SampleScan:
  		case T_ValuesScan:
  		case T_CteScan:
  		case T_WorkTableScan:
***************
*** 1819,1824 **** ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
--- 1824,1830 ----
  	switch (nodeTag(plan))
  	{
  		case T_SeqScan:
+ 		case T_SampleScan:
  		case T_IndexScan:
  		case T_IndexOnlyScan:
  		case T_BitmapHeapScan:
*** a/src/backend/executor/Makefile
--- b/src/backend/executor/Makefile
***************
*** 21,27 **** OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
         nodeLimit.o nodeLockRows.o \
         nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
         nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
!        nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
         nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
         nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
         nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o spi.o
--- 21,27 ----
         nodeLimit.o nodeLockRows.o \
         nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
         nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
!        nodeSeqscan.o nodeSamplescan.o nodeSetOp.o nodeSort.o nodeUnique.o \
         nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
         nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
         nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o spi.o
*** a/src/backend/executor/execAmi.c
--- b/src/backend/executor/execAmi.c
***************
*** 38,43 ****
--- 38,44 ----
  #include "executor/nodeRecursiveunion.h"
  #include "executor/nodeResult.h"
  #include "executor/nodeSeqscan.h"
+ #include "executor/nodeSamplescan.h"
  #include "executor/nodeSetOp.h"
  #include "executor/nodeSort.h"
  #include "executor/nodeSubplan.h"
***************
*** 152,157 **** ExecReScan(PlanState *node)
--- 153,162 ----
  			ExecReScanSeqScan((SeqScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			ExecReScanSampleScan((SampleScanState *) node);
+ 			break;
+ 
  		case T_IndexScanState:
  			ExecReScanIndexScan((IndexScanState *) node);
  			break;
***************
*** 274,279 **** ExecMarkPos(PlanState *node)
--- 279,288 ----
  			ExecSeqMarkPos((SeqScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			ExecSampleMarkPos((SampleScanState *) node);
+ 			break;
+ 
  		case T_IndexScanState:
  			ExecIndexMarkPos((IndexScanState *) node);
  			break;
***************
*** 331,336 **** ExecRestrPos(PlanState *node)
--- 340,349 ----
  			ExecSeqRestrPos((SeqScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			ExecSampleRestrPos((SampleScanState *) node);
+ 			break;
+ 
  		case T_IndexScanState:
  			ExecIndexRestrPos((IndexScanState *) node);
  			break;
***************
*** 383,388 **** ExecSupportsMarkRestore(NodeTag plantype)
--- 396,402 ----
  	switch (plantype)
  	{
  		case T_SeqScan:
+ 		case T_SampleScan:
  		case T_IndexScan:
  		case T_IndexOnlyScan:
  		case T_TidScan:
***************
*** 452,457 **** ExecSupportsBackwardScan(Plan *node)
--- 466,475 ----
  		case T_CteScan:
  			return TargetListSupportsBackwardScan(node->targetlist);
  
+ 		/* For now, TableSample should not do backward */
+ 		case T_SampleScan:
+ 			return false;
+ 
  		case T_IndexScan:
  			return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
  				TargetListSupportsBackwardScan(node->targetlist);
*** a/src/backend/executor/execCurrent.c
--- b/src/backend/executor/execCurrent.c
***************
*** 261,266 **** search_plan_tree(PlanState *node, Oid table_oid)
--- 261,267 ----
  			 * scan nodes can all be treated alike
  			 */
  		case T_SeqScanState:
+ 		case T_SampleScanState:
  		case T_IndexScanState:
  		case T_IndexOnlyScanState:
  		case T_BitmapHeapScanState:
*** a/src/backend/executor/execProcnode.c
--- b/src/backend/executor/execProcnode.c
***************
*** 101,106 ****
--- 101,107 ----
  #include "executor/nodeRecursiveunion.h"
  #include "executor/nodeResult.h"
  #include "executor/nodeSeqscan.h"
+ #include "executor/nodeSamplescan.h"
  #include "executor/nodeSetOp.h"
  #include "executor/nodeSort.h"
  #include "executor/nodeSubplan.h"
***************
*** 188,193 **** ExecInitNode(Plan *node, EState *estate, int eflags)
--- 189,199 ----
  												   estate, eflags);
  			break;
  
+ 		case T_SampleScan:
+ 			result = (PlanState *) ExecInitSampleScan((SampleScan *) node,
+ 													   estate, eflags);
+ 			break;
+ 
  		case T_IndexScan:
  			result = (PlanState *) ExecInitIndexScan((IndexScan *) node,
  													 estate, eflags);
***************
*** 399,404 **** ExecProcNode(PlanState *node)
--- 405,414 ----
  			result = ExecSeqScan((SeqScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			result = ExecSampleScan((SampleScanState *) node);
+ 			break;
+ 
  		case T_IndexScanState:
  			result = ExecIndexScan((IndexScanState *) node);
  			break;
***************
*** 633,638 **** ExecEndNode(PlanState *node)
--- 643,652 ----
  			ExecEndSeqScan((SeqScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			ExecEndSampleScan((SampleScanState *) node);
+ 			break;
+ 
  		case T_IndexScanState:
  			ExecEndIndexScan((IndexScanState *) node);
  			break;
*** /dev/null
--- b/src/backend/executor/nodeSamplescan.c
***************
*** 0 ****
--- 1,345 ----
+ /*-------------------------------------------------------------------------
+  *
+  * nodeSamplescan.c
+  *	  Support routines for sample scans of relations.
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/executor/nodeSamplescan.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ /*
+  * INTERFACE ROUTINES
+  *		ExecSampleScan				randomly scans a relation.
+  *		ExecSampleNext				retrieve next tuple in the order depending on sampling method System/Bernoulli.
+  *		ExecInitSampleScan			creates and initializes a samplescan node.
+  *		ExecEndSampleScan			releases any storage allocated.
+  *		ExecReScanSampleScan		rescans the relation
+  *		ExecSampleMarkPos			marks scan position
+  *		ExecSampleRestrPos			restores scan position
+  */
+ #include "postgres.h"
+ 
+ #include <time.h>
+ 
+ #include "access/relscan.h"
+ #include "executor/execdebug.h"
+ #include "executor/nodeSamplescan.h"
+ #include "utils/rel.h"
+ #include "access/heapam.h"
+ #include "executor/executor.h"
+ #include "parser/parsetree.h"
+ #include "storage/bufmgr.h"
+ 
+ 
+ static void InitScanRelation(SampleScanState *node, EState *estate);
+ static TupleTableSlot *SampleNext(SampleScanState *node);
+ 
+ 
+ 
+ /* ----------------------------------------------------------------
+  *						Scan Support
+  * ----------------------------------------------------------------
+  */
+ 
+ /* ----------------------------------------------------------------
+  *		SampleNext
+  *
+  *		This is a workhorse for ExecSampleScan
+  * ----------------------------------------------------------------
+  */
+ static TupleTableSlot *
+ SampleNext(SampleScanState *node)
+ {
+ 	HeapTuple	tuple;
+ 	HeapScanDesc scandesc;
+ 	EState		 *estate;
+ 	ScanDirection	direction;
+ 	TupleTableSlot *slot;
+ 
+ 	/*
+ 	 * get information from the estate and scan state.
+ 	 */
+ 	scandesc = node->ss.ss_currentScanDesc;
+ 	estate = node->ss.ps.state;
+ 	direction = estate->es_direction;
+ 	slot = node->ss.ss_ScanTupleSlot;
+ 
+ 	tuple = heap_getnext(scandesc, direction); 
+ 
+ 	/*              
+ 	 * save the tuple and the buffer returned to us by the access methods in
+ 	 * our scan tuple slot and return the slot.  Note: we pass 'false' because
+ 	 * tuples returned by heap_getnext() are pointers onto disk pages and were
+ 	 * not created with palloc() and so should not be pfree()'d.  Note also
+ 	 * that ExecStoreTuple will increment the refcount of the buffer; the
+ 	 * refcount will not be dropped until the tuple table slot is cleared.
+ 	 */
+ 	if (tuple)
+ 		ExecStoreTuple(tuple,	/* tuple to store */
+ 					   slot,	/* slot to store in */
+ 					   scandesc->rs_cbuf,		/* buffer associated with this
+ 												 * tuple */
+ 					   false);	/* don't pfree this pointer */
+ 	else
+ 		ExecClearTuple(slot);
+ 
+ 	return slot;
+ }
+ 
+ /*
+  * SampleRecheck -- access method routine to recheck a tuple in EvalPlanQual.
+  * Same as SeqScan, should modify?
+  */
+ static bool
+ SampleRecheck(SampleScanState *node, TupleTableSlot *slot)
+ {
+ 	return true;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecSampleScan(node)
+  *
+  *		Return the next tuple in the sample scan's result set
+  *		We call the ExecScan() routine and pass it the appropriate
+  *		access method functions.
+  * ----------------------------------------------------------------
+  */
+ TupleTableSlot *
+ ExecSampleScan(SampleScanState *node)
+ {
+ 	return ExecScan((ScanState *) node,
+ 				(ExecScanAccessMtd) SampleNext,
+ 				(ExecScanRecheckMtd) SampleRecheck);
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		InitScanRelation
+  *
+  *		This does the initialization for scan relations and
+  *		subplans of scans.
+  * ----------------------------------------------------------------
+  */
+ static void
+ InitScanRelation(SampleScanState *node, EState *estate)
+ {
+ 	Relation	currentRelation;
+ 	HeapScanDesc currentScanDesc;
+ 
+ 	/*
+ 	 * get the relation object id from the relid'th entry in the range table,
+ 	 * open that relation and acquire appropriate lock on it.
+ 	 */
+ 	currentRelation = ExecOpenScanRelation(estate,
+ 									 ((SampleScan *) node->ss.ps.plan)->scan.scanrelid);
+ 
+ 	currentScanDesc = heap_beginscan(currentRelation,
+ 									 estate->es_snapshot,
+ 									 0,
+ 									 NULL);
+ 
+ 	node->ss.ss_currentRelation = currentRelation;
+ 	node->ss.ss_currentScanDesc = currentScanDesc;
+ 
+ 	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecInitSampleScan
+  * ----------------------------------------------------------------
+  */
+ SampleScanState *
+ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
+ {
+ 	SampleScanState *scanstate;
+ 	TableSampleMethod sample_method = node->sample_info->sample_method;
+ 	int				  sample_percent = node->sample_info->sample_percent;
+ 	double				  seed;
+ 
+ 	/*
+ 	 * We don't expect to have any child plan node
+ 	 */
+ 	Assert(outerPlan(node) == NULL);
+ 	Assert(innerPlan(node) == NULL);
+ 
+ 	/*
+ 	 * create state structure
+ 	 */
+ 	scanstate = makeNode(SampleScanState);
+ 	scanstate->ss.ps.plan = (Plan *) node;
+ 	scanstate->ss.ps.state = estate;
+ 
+ 	/*
+ 	 * Miscellaneous initialization
+ 	 *
+ 	 * create expression context for node
+ 	 */
+ 	ExecAssignExprContext(estate, &scanstate->ss.ps);
+ 
+ 	/*
+ 	 * 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);
+ 
+ 	/*
+ 	 * tuple table initialization
+ 	 */
+ 	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+ 	ExecInitScanTupleSlot(estate, &scanstate->ss);
+ 
+ 	/*
+ 	 * initialize scan relation
+ 	 */
+ 	InitScanRelation(scanstate, estate);
+ 
+ 	/*
+ 	 * InitScanRelation cannot see sampleinfo, initialize below
+ 	 */
+ 	HeapScanDesc scan = scanstate->ss.ss_currentScanDesc;
+ 
+ 	scan->is_sample_scan = true;
+ 	scan->sample_percent = sample_percent;
+ 	scan->sample_method = sample_method;
+ 
+ 	scanstate->ss.ps.ps_TupFromTlist = false;
+ 
+ 	/*
+ 	 * Initialize result tuple type and projection info.
+ 	 */
+ 	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+ 	ExecAssignScanProjectionInfo(&scanstate->ss);
+ 
+ 	/*
+ 	 * Initialize fields of SampleScanState for BERNOULLI
+ 	 */
+ 	if(sample_method == SAMPLE_BERNOULLI)
+ 	{
+ 		double numtuples = scan->rs_rd->rd_rel->reltuples;
+ 		int targrows = (int)floor(numtuples*sample_percent/100 + 0.5);
+ 
+ 		scan->targrows = targrows;
+ 		scan->rs_samplerows = (HeapTuple *)palloc(targrows * sizeof(HeapTuple));
+ 		scan->rs_curindex = 0;
+ 		scan->rs_samplesize = 0;
+ 		scan->rs_sampleinited = false;
+ 	}
+ 
+ 	/*
+ 	 * Setup PRNG state; seed with the REPEATABLE clause, if any. We
+ 	 * can't just use srandom(), since there could be multiple
+ 	 * concurrent sample scans.
+ 	 *
+ 	 * XXX: using time() to seed the PRNG in the non-repeatable case
+ 	 * could probably be improved. Different state array sizes could
+ 	 * also be tried: do we need high-quality random numbers?
+ 	 */
+ 	if(node->sample_info->is_repeatable)
+ 		seed = node->sample_info->repeat_seed;
+ 	else
+ 		seed = time(NULL);
+ 
+ 	sample_set_seed(scan->rs_randstate, seed);
+ 
+ 	return scanstate;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *						Join Support
+  * XXX: This part needs more work.
+  * 1. To understand , read at execAmi.c, ExecSupportsMarkRestore
+  * 2. The below part is still seqscan code, not changed yet.
+  * 3. ReScan is needed by join methods, Others used by MergeJoin
+  * 4. In Optimizer, the Join support also needs update, like row 
+  *    estimate, cost estimate, and join cost estimate, needs to 
+  *    go better.
+  * ----------------------------------------------------------------
+  */
+ 
+ /* ----------------------------------------------------------------
+  *		ExecReScanSampleScan
+  *
+  *		Rescans the relation.
+  * ----------------------------------------------------------------
+  */
+ void
+ ExecReScanSampleScan(SampleScanState *node)
+ {
+ 	HeapScanDesc scan;
+ 
+ 	scan = node->ss.ss_currentScanDesc;
+ 
+ 	heap_rescan(scan,			/* scan desc */
+ 				NULL);			/* new scan keys */
+ 
+ 	ExecScanReScan((ScanState *) node);
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecSampleMarkPos(node)
+  *
+  *		Marks scan position.
+  * ----------------------------------------------------------------
+  */
+ void
+ ExecSampleMarkPos(SampleScanState *node)
+ {
+ 	HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ 
+ 	heap_markpos(scan);
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecSampleRestrPos
+  *
+  *		Restores scan position.
+  * ----------------------------------------------------------------
+  */
+ void
+ ExecSampleRestrPos(SampleScanState *node)
+ {
+ 	HeapScanDesc scan = node->ss.ss_currentScanDesc;
+ 
+ 	/*
+ 	 * Clear any reference to the previously returned tuple.  This is needed
+ 	 * because the slot is simply pointing at scan->rs_cbuf, which
+ 	 * heap_restrpos will change; we'd have an internally inconsistent slot if
+ 	 * we didn't do this.
+ 	 */
+ 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+ 
+ 	heap_restrpos(scan);
+ }
+ 
+ /*
+  * Shutdown this scan. This function should generally be symmetric with
+  * ExecInitSampleScan(): we ought to clean up after ourselves.
+  */
+ void
+ ExecEndSampleScan(SampleScanState *node)
+ {
+ 	ExecFreeExprContext(&node->ss.ps);
+ 
+ 	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+ 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+ 
+ 	/*
+ 	 * Close heap scan
+ 	 */
+ 	heap_endscan(node->ss.ss_currentScanDesc);
+ 
+ 	/*
+ 	 * Note that ExecCloseScanRelation() does NOT release the lock we
+ 	 * acquired on the scan relation: it is held until the end of the
+ 	 * transaction.
+ 	 */
+ 	ExecCloseScanRelation(node->ss.ss_currentRelation);
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 350,355 **** _copySeqScan(const SeqScan *from)
--- 350,373 ----
  }
  
  /*
+  * _copySampleScan
+  */
+ static SampleScan *
+ _copySampleScan(const SampleScan *from)
+ {
+ 	SampleScan *newnode = makeNode(SampleScan);
+ 
+ 	/*
+ 	 * copy node superclass fields
+ 	 */
+ 	CopyScanFields((const Scan *) from, (Scan *) newnode);
+ 
+ 	COPY_NODE_FIELD(sample_info);
+ 
+ 	return newnode;
+ }
+ 
+ /*
   * _copyIndexScan
   */
  static IndexScan *
***************
*** 1013,1018 **** _copyRangeVar(const RangeVar *from)
--- 1031,1037 ----
  	COPY_SCALAR_FIELD(inhOpt);
  	COPY_SCALAR_FIELD(relpersistence);
  	COPY_NODE_FIELD(alias);
+ 	COPY_NODE_FIELD(sample_info);
  	COPY_LOCATION_FIELD(location);
  
  	return newnode;
***************
*** 1804,1809 **** _copyFromExpr(const FromExpr *from)
--- 1823,1846 ----
  	return newnode;
  }
  
+ 
+ /*
+  * _CopyTableSampleInfo
+  */
+ static TableSampleInfo *
+ _copyTableSampleInfo(const TableSampleInfo *from)
+ {
+ 	TableSampleInfo *newnode = makeNode(TableSampleInfo);
+ 
+ 	COPY_SCALAR_FIELD(sample_percent);
+ 	COPY_SCALAR_FIELD(sample_method);
+ 	COPY_SCALAR_FIELD(is_repeatable);
+ 	COPY_SCALAR_FIELD(repeat_seed);
+ 
+ 	return newnode;
+ }
+ 
+ 
  /* ****************************************************************
   *						relation.h copy functions
   *
***************
*** 1955,1960 **** _copyRangeTblEntry(const RangeTblEntry *from)
--- 1992,1998 ----
  	COPY_SCALAR_FIELD(rtekind);
  	COPY_SCALAR_FIELD(relid);
  	COPY_SCALAR_FIELD(relkind);
+ 	COPY_NODE_FIELD(sample_info);
  	COPY_NODE_FIELD(subquery);
  	COPY_SCALAR_FIELD(security_barrier);
  	COPY_SCALAR_FIELD(jointype);
***************
*** 3867,3872 **** copyObject(const void *from)
--- 3905,3913 ----
  		case T_ValuesScan:
  			retval = _copyValuesScan(from);
  			break;
+ 		case T_SampleScan:
+ 			retval = _copySampleScan(from);
+ 			break;
  		case T_CteScan:
  			retval = _copyCteScan(from);
  			break;
***************
*** 4066,4071 **** copyObject(const void *from)
--- 4107,4115 ----
  		case T_FromExpr:
  			retval = _copyFromExpr(from);
  			break;
+ 		case T_TableSampleInfo:
+ 			retval = _copyTableSampleInfo(from);
+ 			break;
  
  			/*
  			 * RELATION NODES
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 107,112 **** _equalRangeVar(const RangeVar *a, const RangeVar *b)
--- 107,113 ----
  	COMPARE_SCALAR_FIELD(relpersistence);
  	COMPARE_NODE_FIELD(alias);
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_NODE_FIELD(sample_info);
  
  	return true;
  }
***************
*** 777,782 **** _equalFromExpr(const FromExpr *a, const FromExpr *b)
--- 778,794 ----
  	return true;
  }
  
+ static bool
+ _equalTableSampleInfo(TableSampleInfo *a, TableSampleInfo *b)
+ {
+ 	COMPARE_SCALAR_FIELD(sample_percent);
+ 	COMPARE_SCALAR_FIELD(sample_method);
+ 	COMPARE_SCALAR_FIELD(is_repeatable);
+ 	COMPARE_SCALAR_FIELD(repeat_seed);
+ 
+ 	return true;
+ }
+ 
  
  /*
   * Stuff from relation.h
***************
*** 2271,2276 **** _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
--- 2283,2289 ----
  	COMPARE_SCALAR_FIELD(rtekind);
  	COMPARE_SCALAR_FIELD(relid);
  	COMPARE_SCALAR_FIELD(relkind);
+ 	COMPARE_NODE_FIELD(sample_info);
  	COMPARE_NODE_FIELD(subquery);
  	COMPARE_SCALAR_FIELD(security_barrier);
  	COMPARE_SCALAR_FIELD(jointype);
***************
*** 2630,2635 **** equal(const void *a, const void *b)
--- 2643,2651 ----
  		case T_JoinExpr:
  			retval = _equalJoinExpr(a, b);
  			break;
+ 		case T_TableSampleInfo:
+ 			retval = _equalTableSampleInfo(a, b);
+ 			break;
  
  			/*
  			 * RELATION NODES
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 437,442 **** _outSeqScan(StringInfo str, const SeqScan *node)
--- 437,452 ----
  }
  
  static void
+ _outSampleScan(StringInfo str, const SampleScan *node)
+ {
+ 	WRITE_NODE_TYPE("SAMPLESCAN");
+ 
+ 	_outScanInfo(str, (const Scan *) node);
+ 
+ 	WRITE_NODE_FIELD(sample_info);
+ }
+ 
+ static void
  _outIndexScan(StringInfo str, const IndexScan *node)
  {
  	WRITE_NODE_TYPE("INDEXSCAN");
***************
*** 880,885 **** _outRangeVar(StringInfo str, const RangeVar *node)
--- 890,896 ----
  	WRITE_CHAR_FIELD(relpersistence);
  	WRITE_NODE_FIELD(alias);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_NODE_FIELD(sample_info);
  }
  
  static void
***************
*** 1749,1754 **** _outRelOptInfo(StringInfo str, const RelOptInfo *node)
--- 1760,1766 ----
  	WRITE_FLOAT_FIELD(allvisfrac, "%.6f");
  	WRITE_NODE_FIELD(subplan);
  	WRITE_NODE_FIELD(subroot);
+ 	WRITE_BOOL_FIELD(has_table_sample);
  	/* we don't try to print fdwroutine or fdw_private */
  	WRITE_NODE_FIELD(baserestrictinfo);
  	WRITE_NODE_FIELD(joininfo);
***************
*** 2330,2335 **** _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
--- 2342,2348 ----
  		case RTE_RELATION:
  			WRITE_OID_FIELD(relid);
  			WRITE_CHAR_FIELD(relkind);
+ 			WRITE_NODE_FIELD(sample_info);
  			break;
  		case RTE_SUBQUERY:
  			WRITE_NODE_FIELD(subquery);
***************
*** 2372,2377 **** _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
--- 2385,2401 ----
  }
  
  static void
+ _outTableSampleInfo(StringInfo str, TableSampleInfo *node)
+ {
+ 	WRITE_NODE_TYPE("TABLESAMPLE");
+ 
+ 	WRITE_INT_FIELD(sample_percent);
+ 	WRITE_ENUM_FIELD(sample_method, TableSampleMethod);
+ 	WRITE_INT_FIELD(repeat_seed);
+ 	WRITE_BOOL_FIELD(is_repeatable);
+ }
+ 
+ static void
  _outAExpr(StringInfo str, const A_Expr *node)
  {
  	WRITE_NODE_TYPE("AEXPR");
***************
*** 2762,2767 **** _outNode(StringInfo str, const void *obj)
--- 2786,2794 ----
  			case T_ValuesScan:
  				_outValuesScan(str, obj);
  				break;
+ 			case T_SampleScan:
+ 				_outSampleScan(str, obj);
+ 				break;
  			case T_CteScan:
  				_outCteScan(str, obj);
  				break;
***************
*** 3148,3153 **** _outNode(StringInfo str, const void *obj)
--- 3175,3183 ----
  			case T_Constraint:
  				_outConstraint(str, obj);
  				break;
+ 			case T_TableSampleInfo:
+ 				_outTableSampleInfo(str, obj);
+ 				break;
  			case T_FuncCall:
  				_outFuncCall(str, obj);
  				break;
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 380,385 **** _readRangeVar(void)
--- 380,399 ----
  	READ_CHAR_FIELD(relpersistence);
  	READ_NODE_FIELD(alias);
  	READ_LOCATION_FIELD(location);
+ 	READ_NODE_FIELD(sample_info);
+ 
+ 	READ_DONE();
+ }
+ 
+ static TableSampleInfo *
+ _readTableSampleInfo(void)
+ {
+ 	READ_LOCALS(TableSampleInfo);
+ 
+ 	READ_INT_FIELD(sample_percent);
+ 	READ_ENUM_FIELD(sample_method, TableSampleMethod);
+ 	READ_BOOL_FIELD(is_repeatable);
+ 	READ_INT_FIELD(repeat_seed);
  
  	READ_DONE();
  }
***************
*** 1189,1194 **** _readRangeTblEntry(void)
--- 1203,1209 ----
  		case RTE_RELATION:
  			READ_OID_FIELD(relid);
  			READ_CHAR_FIELD(relkind);
+ 			READ_NODE_FIELD(sample_info);
  			break;
  		case RTE_SUBQUERY:
  			READ_NODE_FIELD(subquery);
***************
*** 1270,1275 **** parseNodeString(void)
--- 1285,1292 ----
  		return_value = _readAlias();
  	else if (MATCH("RANGEVAR", 8))
  		return_value = _readRangeVar();
+ 	else if (MATCH("TABLESAMPLE", 11))
+ 		return_value = _readTableSampleInfo();
  	else if (MATCH("INTOCLAUSE", 10))
  		return_value = _readIntoClause();
  	else if (MATCH("VAR", 3))
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
***************
*** 376,389 **** set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  static void
  set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
! 	/* Consider sequential scan */
! 	add_path(rel, create_seqscan_path(root, rel, NULL));
  
! 	/* Consider index scans */
! 	create_index_paths(root, rel);
  
! 	/* Consider TID scans */
! 	create_tidscan_paths(root, rel);
  
  	/* Now find the cheapest of the paths for this rel */
  	set_cheapest(rel);
--- 376,403 ----
  static void
  set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
! 	/*
! 	 * If there's a TABLESAMPLE clause, we ONLY consider using a SampleScan.
! 	 * This could be improved: in some cicumstances it might make sense to do
! 	 * an IndexScan and then sample from the index scan's result set, for instance.
! 	 */
! 	if(rel->has_table_sample){
! 		/* We decide we are inside a cursor stmt. If yes, abort */
! 		if(root->glob->isCursorStmt)
! 			elog(ERROR,
! 					"Unsupported Cursor: No Tablesample should be queried under cursor!");
! 		add_path(rel, create_samplescan_path(root, rel));
! 	}else
! 	{
! 		/* Consider sequential scan */
! 		add_path(rel, create_seqscan_path(root, rel, NULL));
  
! 		/* Consider index scans */
! 		create_index_paths(root, rel);
  
! 		/* Consider TID scans */
! 		create_tidscan_paths(root, rel);
! 	}
  
  	/* Now find the cheapest of the paths for this rel */
  	set_cheapest(rel);
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
***************
*** 103,108 **** int			effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
--- 103,109 ----
  Cost		disable_cost = 1.0e10;
  
  bool		enable_seqscan = true;
+ bool		enable_samplescan = true;
  bool		enable_indexscan = true;
  bool		enable_indexonlyscan = true;
  bool		enable_bitmapscan = true;
***************
*** 214,219 **** cost_seqscan(Path *path, PlannerInfo *root,
--- 215,263 ----
  	path->total_cost = startup_cost + run_cost;
  }
  
+ 
+ /*
+  * cost_samplescan
+  *	  Determines and returns the cost of scanning a relation according to sample scan.
+  */
+ void
+ cost_samplescan(Path *path, PlannerInfo *root,
+ 			 RelOptInfo *baserel)
+ {
+ 	Cost			startup_cost = 0;
+ 	Cost			run_cost = 0;
+ 	Cost			cpu_per_tuple;
+ 	RangeTblEntry	*rte;
+ 	int				sample_percent;
+ 
+ 	/* Should only be applied to base relations */
+ 	Assert(baserel->relid > 0);
+ 	Assert(baserel->rtekind == RTE_RELATION);
+ 	Assert(path->pathtype == T_SampleScan);
+ 
+ 	rte = planner_rt_fetch(baserel->relid, root);
+ 	sample_percent = rte->sample_info->sample_percent;
+ 
+ 	/*
+ 	 * disk costs
+ 	 * When the sample percentage is close to 100, we're likely to
+ 	 * be doing purely sequantial I/O. Conversely, for small percentage
+ 	 * samples, we're doing random I/O. Fow now, just be conservative
+ 	 * and always assume that we need to do a random I/O for each
+ 	 * sampled block. Of course, this is quite bogus.
+ 	 */
+ 	run_cost += random_page_cost * baserel->pages * sample_percent/100;
+ 
+ 	/* CPU costs */
+ 	startup_cost += baserel->baserestrictcost.startup;
+ 	cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
+ 	run_cost += cpu_per_tuple * baserel->tuples;
+ 
+ 	path->startup_cost = startup_cost;
+ 	path->total_cost = startup_cost + run_cost;
+ }
+ 
+ 
  /*
   * cost_index
   *	  Determines and returns the cost of scanning a relation using an index.
***************
*** 3416,3422 **** approx_tuple_count(PlannerInfo *root, JoinPath *path, List *quals)
   *
   * We set the following fields of the rel node:
   *	rows: the estimated number of output tuples (after applying
!  *		  restriction clauses).
   *	width: the estimated average output tuple width in bytes.
   *	baserestrictcost: estimated cost of evaluating baserestrictinfo clauses.
   */
--- 3460,3466 ----
   *
   * We set the following fields of the rel node:
   *	rows: the estimated number of output tuples (after applying
!  *		  restriction clauses and considering the effect of TABLESAMPLE).
   *	width: the estimated average output tuple width in bytes.
   *	baserestrictcost: estimated cost of evaluating baserestrictinfo clauses.
   */
***************
*** 3424,3429 **** void
--- 3468,3474 ----
  set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
  {
  	double		nrows;
+ 	RangeTblEntry	*rte;
  
  	/* Should only be applied to base relations */
  	Assert(rel->relid > 0);
***************
*** 3435,3440 **** set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
--- 3480,3496 ----
  							   JOIN_INNER,
  							   NULL);
  
+ 	/* Consider TABLESAMPLE, if any. We assume the live heap rows are
+ 	 * uniformly distributed over the heap: this is a bogus simplifying
+ 	 * assumption. Note that the executor will apply the TABLESAMPLE
+ 	 * clause before applying any restrictions, we assume that the 
+ 	 * restrictions have the same selectivity for the sampled sub-relation
+ 	 * as they do for the entire relation (which is somewhat reasonable).
+ 	 */
+ 	rte = planner_rt_fetch(rel->relid, root);
+ 	if(rte->sample_info)
+ 		nrows = nrows*(rte->sample_info->sample_percent/100);
+ 
  	rel->rows = clamp_row_est(nrows);
  
  	cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 55,60 **** static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path
--- 55,62 ----
  static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
  static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
  					List *tlist, List *scan_clauses);
+ static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path,
+ 						List *tlist, List *scan_clauses);
  static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
  					  List *tlist, List *scan_clauses, bool indexonly);
  static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
***************
*** 93,98 **** static List *order_qual_clauses(PlannerInfo *root, List *clauses);
--- 95,101 ----
  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(PlannerInfo *root, 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,
***************
*** 214,219 **** create_plan_recurse(PlannerInfo *root, Path *best_path)
--- 217,223 ----
  	switch (best_path->pathtype)
  	{
  		case T_SeqScan:
+ 		case T_SampleScan:
  		case T_IndexScan:
  		case T_IndexOnlyScan:
  		case T_BitmapHeapScan:
***************
*** 326,331 **** create_scan_plan(PlannerInfo *root, Path *best_path)
--- 330,342 ----
  												scan_clauses);
  			break;
  
+ 		case T_SampleScan:
+ 			plan = (Plan *) create_samplescan_plan(root,
+ 													best_path,
+ 													tlist,
+ 													scan_clauses);
+ 			break;
+ 
  		case T_IndexScan:
  			plan = (Plan *) create_indexscan_plan(root,
  												  (IndexPath *) best_path,
***************
*** 512,517 **** disuse_physical_tlist(Plan *plan, Path *path)
--- 523,529 ----
  	switch (path->pathtype)
  	{
  		case T_SeqScan:
+ 		case T_SampleScan:
  		case T_IndexScan:
  		case T_IndexOnlyScan:
  		case T_BitmapHeapScan:
***************
*** 1089,1094 **** create_seqscan_plan(PlannerInfo *root, Path *best_path,
--- 1101,1146 ----
  }
  
  /*
+  * 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, Path *best_path,
+ 					List *tlist, List *scan_clauses)
+ {
+ 	SampleScan    *scan_plan;
+ 	Index		scan_relid = best_path->parent->relid;
+ 
+ 	/* it should be a base rel... */
+ 	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);
+ 
+ 	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+ 	scan_clauses = extract_actual_clauses(scan_clauses, false);
+ 
+ 	/* Replace any outer-relation variables with nestloop params */
+ 	if (best_path->param_info)
+ 	{
+ 		scan_clauses = (List *)
+ 			replace_nestloop_params(root, (Node *) scan_clauses);
+ 	}
+ 
+ 	scan_plan = make_samplescan(root, tlist,
+ 							 scan_clauses,
+ 							 scan_relid);
+ 
+ 	copy_path_costsize(&scan_plan->scan.plan, best_path);
+ 
+ 	return scan_plan;
+ }
+ 
+ 
+ /*
   * create_indexscan_plan
   *	  Returns an indexscan plan for the base relation scanned by 'best_path'
   *	  with restriction clauses 'scan_clauses' and targetlist 'tlist'.
***************
*** 3167,3172 **** make_seqscan(List *qptlist,
--- 3219,3253 ----
  	return node;
  }
  
+ 
+ static SampleScan *
+ make_samplescan(PlannerInfo *root,
+ 			 List *qptlist,
+ 			 List *qpqual,
+ 			 Index scanrelid)
+ {
+ 	SampleScan    *node = makeNode(SampleScan);
+ 	Plan	   *plan = &node->scan.plan;
+ 	RangeTblEntry *rte;
+ 
+ 	/* cost should be inserted by caller */
+ 	plan->targetlist = qptlist;
+ 	plan->qual = qpqual;
+ 	plan->lefttree = NULL;
+ 	plan->righttree = NULL;
+ 	node->scan.scanrelid = scanrelid;
+ 
+ 	/*
+ 	 * For the convenience of nodeSamplescan.c, we stash a pointer to
+ 	 * the TableSampleInfo for this SampleScan in the SampleScan's
+ 	 * plan node.
+ 	 */
+ 	rte = planner_rt_fetch(scanrelid, root);
+ 	node->sample_info = rte->sample_info;
+ 
+ 	return node;
+ }
+ 
  static IndexScan *
  make_indexscan(List *qptlist,
  			   List *qpqual,
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 163,168 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
--- 163,171 ----
  	glob->lastPHId = 0;
  	glob->lastRowMarkId = 0;
  	glob->transientPlan = false;
+ 	if(cursorOptions)
+ 		glob->isCursorStmt = true;
+ 	else glob->isCursorStmt = false;
  
  	/* Determine what fraction of the plan is likely to be scanned */
  	if (cursorOptions & CURSOR_OPT_FAST_PLAN)
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 306,311 **** set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
--- 306,322 ----
  					fix_scan_list(root, splan->plan.qual, rtoffset);
  			}
  			break;
+ 		case T_SampleScan:
+ 			{
+ 				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);
+ 			}
+ 			break;
  		case T_IndexScan:
  			{
  				IndexScan  *splan = (IndexScan *) plan;
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 2082,2087 **** finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
--- 2082,2091 ----
  			context.paramids = bms_add_members(context.paramids, scan_params);
  			break;
  
+ 		case T_SampleScan:
+ 			context.paramids = bms_add_members(context.paramids, scan_params);
+ 			break;
+ 
  		case T_IndexScan:
  			finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
  							  &context);
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 747,752 **** create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
--- 747,774 ----
  	return pathnode;
  }
  
+ 
+ /*
+  * create_samplescan_path
+  *	  Creates a path corresponding to a sample scan, returning the
+  *	  pathnode.
+  *	  Forked from create_seqscan_path, maybe there are other attributes needed.
+  */
+ Path *
+ create_samplescan_path(PlannerInfo *root, RelOptInfo *rel)
+ {
+ 	Path	   *pathnode = makeNode(Path);
+ 
+ 	pathnode->pathtype = T_SampleScan;
+ 	pathnode->parent = rel;
+ 	pathnode->pathkeys = NIL;
+ 
+ 	cost_samplescan(pathnode, root, rel);
+ 
+ 	return pathnode;
+ }
+ 
+ 
  /*
   * create_index_path
   *	  Creates a path node for an index scan.
***************
*** 2072,2077 **** reparameterize_path(PlannerInfo *root, Path *path,
--- 2094,2101 ----
  	{
  		case T_SeqScan:
  			return create_seqscan_path(root, rel, required_outer);
+ 		case T_SampleScan:
+ 			return create_samplescan_path(root, rel);
  		case T_IndexScan:
  		case T_IndexOnlyScan:
  			{
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
***************
*** 117,122 **** build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
--- 117,123 ----
  	rel->subroot = NULL;
  	rel->fdwroutine = NULL;
  	rel->fdw_private = NULL;
+ 	rel->has_table_sample = (rte->sample_info != NULL);
  	rel->baserestrictinfo = NIL;
  	rel->baserestrictcost.startup = 0;
  	rel->baserestrictcost.per_tuple = 0;
***************
*** 373,378 **** build_join_rel(PlannerInfo *root,
--- 374,380 ----
  	joinrel->subroot = NULL;
  	joinrel->fdwroutine = NULL;
  	joinrel->fdw_private = NULL;
+ 	joinrel->has_table_sample = false;
  	joinrel->baserestrictinfo = NIL;
  	joinrel->baserestrictcost.startup = 0;
  	joinrel->baserestrictcost.per_tuple = 0;
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
***************
*** 412,417 **** transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
--- 412,426 ----
  	/* There can't be any outer WITH to worry about */
  	Assert(pstate->p_ctenamespace == NIL);
  
+ 	/*
+ 	 * This should be disallowed by the parser, but check anyway for
+ 	 * the sake of paranoia.
+ 	 */
+ 	if (stmt->relation->sample_info)
+ 		ereport(ERROR,
+ 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				errmsg("TABLESAMPLE cannot be specified for INSERT")));
+ 
  	qry->commandType = CMD_INSERT;
  	pstate->p_is_insert = true;
  
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 402,407 **** static void processCASbits(int cas_bits, int location, const char *constrType,
--- 402,411 ----
  %type <ielem>	index_elem
  %type <node>	table_ref
  %type <jexpr>	joined_table
+ %type <node>	opt_table_sample
+ %type <range>	relation_expr_opt_sample
+ %type <value>	sample_method
+ %type <value>	opt_repeatable_clause
  %type <range>	relation_expr
  %type <range>	relation_expr_opt_alias
  %type <target>	target_el single_set_clause set_target insert_column_item
***************
*** 497,503 **** static void processCASbits(int cas_bits, int location, const char *constrType,
  	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
  	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
  
! 	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
  	BOOLEAN_P BOTH BY
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
--- 501,507 ----
  	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
  	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
  
! 	BACKWARD BEFORE BEGIN_P BERNOULLI BETWEEN BIGINT BINARY BIT
  	BOOLEAN_P BOTH BY
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
***************
*** 563,569 **** static void processCASbits(int cas_bits, int location, const char *constrType,
  	STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
  	SYMMETRIC SYSID SYSTEM_P
  
! 	TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
  	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P TYPES_P
  
--- 567,573 ----
  	STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
  	SYMMETRIC SYSID SYSTEM_P
  
! 	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
  	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P TYPES_P
  
***************
*** 9312,9318 **** from_list:
  /*
   * table_ref is where an alias clause can be attached.
   */
! table_ref:	relation_expr opt_alias_clause
  				{
  					$1->alias = $2;
  					$$ = (Node *) $1;
--- 9316,9322 ----
  /*
   * table_ref is where an alias clause can be attached.
   */
! table_ref:  relation_expr_opt_sample opt_alias_clause
  				{
  					$1->alias = $2;
  					$$ = (Node *) $1;
***************
*** 9577,9582 **** join_qual:	USING '(' name_list ')'					{ $$ = (Node *) $3; }
--- 9581,9647 ----
  			| ON a_expr								{ $$ = $2; }
  		;
  
+ /*
+  * We want to allow the TABLESAMPLE clause to be specified for 
+  * SELECT, DELETE, and UPDATE, but not for DDL commands. Therefore,
+  * we add a new production that is "relation_expr + optional TABLESAMPLE",
+  * and use that anywhere we'd like to allow a TABLESAMPLE clause to be specified.
+  */
+ 
+ relation_expr_opt_sample:
+ 			relation_expr opt_table_sample
+ 			{
+ 				$$ = $1;
+ 				$$->sample_info = (TableSampleInfo *) $2;
+ 			}
+ 		;
+ 
+ opt_table_sample:
+ 			TABLESAMPLE sample_method '('Iconst')' opt_repeatable_clause
+ 			{
+ 				TableSampleInfo *n = makeNode(TableSampleInfo);
+ 
+ 				if( $2 == 1 )
+ 					n->sample_method = SAMPLE_BERNOULLI;
+ 				else if( $2 == 2 )
+ 					n->sample_method = SAMPLE_SYSTEM;
+ 				else
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 							 errmsg("Sampling method not supported")));
+ 
+ 				n->sample_percent = $4;
+ 				if($4 > 100)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_SAMPLE_SIZE),
+ 							 errmsg("TABLESAMPLE percentage"
+ 									"be greater than 100")));
+ 				if($4 < 0)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_SAMPLE_SIZE),
+ 							 errmsg("TABLESAMPLE percentage must"
+ 									"be greater than 0")));
+ 
+ 				if($6 != NULL)
+ 				{
+ 					n->is_repeatable = true;
+ 					n->repeat_seed = intVal($6);
+ 				}
+ 
+ 				$$ = (Node *)n;
+ 			}
+ 			| /*EMPTY*/				{ $$ = NULL; }
+ 		;
+ 
+ sample_method:
+ 			BERNOULLI				{ $$ = 1; }
+ 			| SYSTEM_P 				{ $$ = 2; }
+ 		;
+ 
+ opt_repeatable_clause:
+ 			REPEATABLE '('Iconst')' { $$ = makeInteger($3); }
+ 			| /*EMPTY*/				{ $$ = NULL; }
+ 		;
  
  relation_expr:
  			qualified_name
***************
*** 9625,9642 **** relation_expr_list:
   * has, causing the parser to prefer to reduce, in effect assuming that the
   * SET is not an alias.
   */
! relation_expr_opt_alias: relation_expr					%prec UMINUS
  				{
  					$$ = $1;
  				}
! 			| relation_expr ColId
  				{
  					Alias *alias = makeNode(Alias);
  					alias->aliasname = $2;
  					$1->alias = alias;
  					$$ = $1;
  				}
! 			| relation_expr AS ColId
  				{
  					Alias *alias = makeNode(Alias);
  					alias->aliasname = $3;
--- 9690,9707 ----
   * has, causing the parser to prefer to reduce, in effect assuming that the
   * SET is not an alias.
   */
! relation_expr_opt_alias: relation_expr_opt_sample					%prec UMINUS
  				{
  					$$ = $1;
  				}
! 			| relation_expr_opt_sample ColId
  				{
  					Alias *alias = makeNode(Alias);
  					alias->aliasname = $2;
  					$1->alias = alias;
  					$$ = $1;
  				}
! 			| relation_expr_opt_sample AS ColId
  				{
  					Alias *alias = makeNode(Alias);
  					alias->aliasname = $3;
***************
*** 12385,12390 **** unreserved_keyword:
--- 12450,12456 ----
  			| BACKWARD
  			| BEFORE
  			| BEGIN_P
+ 			| BERNOULLI
  			| BY
  			| CACHE
  			| CALLED
***************
*** 12539,12545 **** unreserved_keyword:
  			| RELATIVE_P
  			| RELEASE
  			| RENAME
- 			| REPEATABLE
  			| REPLACE
  			| REPLICA
  			| RESET
--- 12605,12610 ----
***************
*** 12712,12717 **** type_func_name_keyword:
--- 12777,12783 ----
  			| OVERLAPS
  			| RIGHT
  			| SIMILAR
+ 			| TABLESAMPLE
  			| VERBOSE
  		;
  
***************
*** 12780,12785 **** reserved_keyword:
--- 12846,12852 ----
  			| PLACING
  			| PRIMARY
  			| REFERENCES
+ 			| REPEATABLE
  			| RETURNING
  			| SELECT
  			| SESSION_USER
*** a/src/backend/parser/parse_clause.c
--- b/src/backend/parser/parse_clause.c
***************
*** 190,195 **** setTargetTable(ParseState *pstate, RangeVar *relation,
--- 190,201 ----
  										relation->alias, inh, false);
  	pstate->p_target_rangetblentry = rte;
  
+ 	/*
+ 	 * Minor kludge: addRangeTableEntryForRelation() can't see the
+ 	 * TABLESAMPLE clause, so attach it to the new RTE manually.
+ 	 */
+ 	rte->sample_info = relation->sample_info;
+ 
  	/* assume new rte is at end */
  	rtindex = list_length(pstate->p_rtable);
  	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
*** a/src/backend/parser/parse_relation.c
--- b/src/backend/parser/parse_relation.c
***************
*** 330,336 **** searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
  
  			if (rte->rtekind == RTE_RELATION &&
  				OidIsValid(relId) &&
! 				rte->relid == relId)
  				return rte;
  			if (rte->rtekind == RTE_CTE &&
  				cte != NULL &&
--- 330,337 ----
  
  			if (rte->rtekind == RTE_RELATION &&
  				OidIsValid(relId) &&
! 				rte->relid == relId &&
! 				equal(rte->sample_info, relation->sample_info))
  				return rte;
  			if (rte->rtekind == RTE_CTE &&
  				cte != NULL &&
***************
*** 995,1000 **** addRangeTableEntry(ParseState *pstate,
--- 996,1002 ----
  	rel = parserOpenTable(pstate, relation, lockmode);
  	rte->relid = RelationGetRelid(rel);
  	rte->relkind = rel->rd_rel->relkind;
+ 	rte->sample_info = relation->sample_info;
  
  	/*
  	 * Build the list of effective column names using user-supplied aliases
***************
*** 1055,1060 **** addRangeTableEntryForRelation(ParseState *pstate,
--- 1057,1063 ----
  	rte->alias = alias;
  	rte->relid = RelationGetRelid(rel);
  	rte->relkind = rel->rd_rel->relkind;
+ 	rte->sample_info = NULL;
  
  	/*
  	 * Build the list of effective column names using user-supplied aliases
***************
*** 1110,1115 **** addRangeTableEntryForSubquery(ParseState *pstate,
--- 1113,1119 ----
  
  	rte->rtekind = RTE_SUBQUERY;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = subquery;
  	rte->alias = alias;
  
***************
*** 1189,1194 **** addRangeTableEntryForFunction(ParseState *pstate,
--- 1193,1199 ----
  
  	rte->rtekind = RTE_FUNCTION;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = NULL;
  	rte->funcexpr = funcexpr;
  	rte->funccoltypes = NIL;
***************
*** 1323,1328 **** addRangeTableEntryForValues(ParseState *pstate,
--- 1328,1334 ----
  
  	rte->rtekind = RTE_VALUES;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = NULL;
  	rte->values_lists = exprs;
  	rte->values_collations = collations;
***************
*** 1403,1408 **** addRangeTableEntryForJoin(ParseState *pstate,
--- 1409,1415 ----
  
  	rte->rtekind = RTE_JOIN;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = NULL;
  	rte->jointype = jointype;
  	rte->joinaliasvars = aliasvars;
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 2174,2179 **** deparse_context_for(const char *aliasname, Oid relid)
--- 2174,2180 ----
  	rte = makeNode(RangeTblEntry);
  	rte->rtekind = RTE_RELATION;
  	rte->relid = relid;
+ 	rte->sample_info = NULL;
  	rte->relkind = RELKIND_RELATION;	/* no need for exactness here */
  	rte->eref = makeAlias(aliasname, NIL);
  	rte->lateral = false;
***************
*** 6719,6724 **** get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
--- 6720,6750 ----
  			 */
  			get_from_clause_alias(rte->alias, rte, context);
  		}
+ 
+ 		if (rte->sample_info)
+ 		{
+ 			TableSampleInfo		*sample_info = rte->sample_info;
+ 			const char			*method_name;
+ 
+ 			Assert(rte->rtekind == RTE_RELATION);
+ 
+ 			switch(sample_info->sample_method)
+ 			{
+ 				case SAMPLE_BERNOULLI:
+ 					method_name = "BERNOULLI";
+ 					break;
+ 				case SAMPLE_SYSTEM:
+ 					method_name = "SYSTEM";
+ 					break;
+ 				default:
+ 					elog(ERROR, "unrecognized sample method: %s", 
+ 						 (int) sample_info->sample_method);
+ 					method_name = NULL;
+ 			}
+ 
+ 			appendStringInfo(buf, " TABLESAMPLE %s (%d)",
+ 							 method_name, sample_info->sample_percent);
+ 		}
  	}
  	else if (IsA(jtnode, JoinExpr))
  	{
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
***************
*** 175,180 **** Section: Class 22 - Data Exception
--- 175,181 ----
  22010    E    ERRCODE_INVALID_INDICATOR_PARAMETER_VALUE                      invalid_indicator_parameter_value
  22023    E    ERRCODE_INVALID_PARAMETER_VALUE                                invalid_parameter_value
  2201B    E    ERRCODE_INVALID_REGULAR_EXPRESSION                             invalid_regular_expression
+ 2202H    E    ERRCODE_INVALID_SAMPLE_SIZE                                    invalid_sample_size
  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
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \
!        superuser.o timeout.o tzparser.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 15,21 ----
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
  OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \
!        superuser.o timeout.o tzparser.o sampleutils.o
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
*** /dev/null
--- b/src/backend/utils/misc/sampleutils.c
***************
*** 0 ****
--- 1,272 ----
+ /*-------------------------------------------------------------------------
+  *
+  * sampleutils.c
+  *	  Routines designed for TableSample Clause, containing Vitter's Reservoir
+  *	  Sampling Algo routines. Tablesample System, Bernoulli sampling and analyze
+  *	  is using these apis.
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/misc/sampleutils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "utils/sampleutils.h"
+ #include "storage/procarray.h"
+ #include "storage/bufmgr.h"
+ #include "utils/tqual.h"
+ #include "utils/rel.h"
+ 
+ #define MEM_OVERHEAD 100000000
+ 
+ 
+ /*
+  * BlockSampler_Init -- prepare for random sampling of blocknumbers
+  *
+  * BlockSampler is used for stage one of our new two-stage tuple
+  * sampling mechanism as discussed on pgsql-hackers 2004-04-02 (subject
+  * "Large DB").  It selects a random sample of samplesize blocks out of
+  * the nblocks blocks in the table.  If the table has less than
+  * samplesize blocks, all blocks are selected.
+  *
+  * Since we know the total number of blocks in advance, we can use the
+  * straightforward Algorithm S from Knuth 3.4.2, rather than Vitter's
+  * algorithm.
+  */
+ void
+ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize)
+ {
+ 	bs->N = nblocks;			/* measured table size */
+ 
+ 	/*
+ 	 * If we decide to reduce samplesize for tables that have less or not much
+ 	 * more than samplesize blocks, here is the place to do it.
+ 	 */
+ 	bs->n = samplesize;
+ 	bs->t = 0;					/* blocks scanned so far */
+ 	bs->m = 0;					/* blocks selected so far */
+ }
+ 
+ bool
+ BlockSampler_HasMore(BlockSampler bs)
+ {
+ 	return (bs->t < bs->N) && (bs->m < bs->n);
+ }
+ 
+ BlockNumber
+ BlockSampler_Next(void *state, BlockSampler bs)
+ {
+ 	BlockNumber K = bs->N - bs->t;		/* remaining blocks */
+ 	int			k = bs->n - bs->m;		/* blocks still to sample */
+ 	double		p;				/* probability to skip block */
+ 	double		V;				/* random */
+ 
+ 	Assert(BlockSampler_HasMore(bs));	/* hence K > 0 and k > 0 */
+ 
+ 	if ((BlockNumber) k >= K)
+ 	{
+ 		/* need all the rest */
+ 		bs->m++;
+ 		return bs->t++;
+ 	}
+ 
+ 	/*----------
+ 	 * It is not obvious that this code matches Knuth's Algorithm S.
+ 	 * Knuth says to skip the current block with probability 1 - k/K.
+ 	 * If we are to skip, we should advance t (hence decrease K), and
+ 	 * repeat the same probabilistic test for the next block.  The naive
+ 	 * implementation thus requires an anl_random_fract() call for each block
+ 	 * number.	But we can reduce this to one anl_random_fract() call per
+ 	 * selected block, by noting that each time the while-test succeeds,
+ 	 * we can reinterpret V as a uniform random number in the range 0 to p.
+ 	 * Therefore, instead of choosing a new V, we just adjust p to be
+ 	 * the appropriate fraction of its former value, and our next loop
+ 	 * makes the appropriate probabilistic test.
+ 	 *
+ 	 * We have initially K > k > 0.  If the loop reduces K to equal k,
+ 	 * the next while-test must fail since p will become exactly zero
+ 	 * (we assume there will not be roundoff error in the division).
+ 	 * (Note: Knuth suggests a "<=" loop condition, but we use "<" just
+ 	 * to be doubly sure about roundoff error.)  Therefore K cannot become
+ 	 * less than k, which means that we cannot fail to select enough blocks.
+ 	 *----------
+ 	 */
+ 	V = anl_random_fract(state);
+ 	p = 1.0 - (double) k / (double) K;
+ 	while (V < p)
+ 	{
+ 		/* skip */
+ 		bs->t++;
+ 		K--;					/* keep K == N - t */
+ 
+ 		/* adjust p to be new cutoff point in reduced range */
+ 		p *= 1.0 - (double) k / (double) K;
+ 	}
+ 
+ 	/* select */
+ 	bs->m++;
+ 	return bs->t++;
+ }
+ 
+ 
+ 
+ /*
+  * These two routines embody Algorithm Z from "Random sampling with a
+  * reservoir" by Jeffrey S. Vitter, in ACM Trans. Math. Softw. 11, 1
+  * (Mar. 1985), Pages 37-57.  Vitter describes his algorithm in terms
+  * of the count S of records to skip before processing another record.
+  * It is computed primarily based on t, the number of records already read.
+  * The only extra state needed between calls is W, a random state variable.
+  *
+  * anl_init_selection_state computes the initial W value.
+  *
+  * Given that we've already read t records (t >= n), anl_get_next_S
+  * determines the number of records to skip before the next record is
+  * processed.
+  */
+ double
+ anl_init_selection_state(void *state, int n)
+ {
+ 	/* Initial value of W (for use when Algorithm Z is first applied) */
+ 	return exp(-log(anl_random_fract(state)) / n);
+ }
+ 
+ double
+ anl_get_next_S(void *state, double t, int n, double *stateptr)
+ {
+ 	double		S;
+ 
+ 	/* The magic constant here is T from Vitter's paper */
+ 	if (t <= (22.0 * n))
+ 	{
+ 		/* Process records using Algorithm X until t is large enough */
+ 		double		V,
+ 					quot;
+ 
+ 		V = anl_random_fract(state); /* Generate V */
+ 		S = 0;
+ 		t += 1;
+ 		/* Note: "num" in Vitter's code is always equal to t - n */
+ 		quot = (t - (double) n) / t;
+ 		/* Find min S satisfying (4.1) */
+ 		while (quot > V)
+ 		{
+ 			S += 1;
+ 			t += 1;
+ 			quot *= (t - (double) n) / t;
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* Now apply Algorithm Z */
+ 		double		W = *stateptr;
+ 		double		term = t - (double) n + 1;
+ 
+ 		for (;;)
+ 		{
+ 			double		numer,
+ 						numer_lim,
+ 						denom;
+ 			double		U,
+ 						X,
+ 						lhs,
+ 						rhs,
+ 						y,
+ 						tmp;
+ 
+ 			
+ 			U = anl_random_fract(state); /* Generate U and X */
+ 			X = t * (W - 1.0);
+ 			S = floor(X);		/* S is tentatively set to floor(X) */
+ 			/* Test if U <= h(S)/cg(X) in the manner of (6.3) */
+ 			tmp = (t + 1) / term;
+ 			lhs = exp(log(((U * tmp * tmp) * (term + S)) / (t + X)) / n);
+ 			rhs = (((t + X) / (term + S)) * term) / t;
+ 			if (lhs <= rhs)
+ 			{
+ 				W = rhs / lhs;
+ 				break;
+ 			}
+ 			/* Test if U <= f(S)/cg(X) */
+ 			y = (((U * (t + 1)) / term) * (t + S + 1)) / (t + X);
+ 			if ((double) n < S)
+ 			{
+ 				denom = t;
+ 				numer_lim = term + S;
+ 			}
+ 			else
+ 			{
+ 				denom = t - (double) n + S;
+ 				numer_lim = t + 1;
+ 			}
+ 			for (numer = t + S; numer >= numer_lim; numer -= 1)
+ 			{
+ 				y *= numer / denom;
+ 				denom -= 1;
+ 			}
+ 			W = exp(-log(anl_random_fract(state)) / n);		/* Generate W in advance */
+ 			if (exp(log(y) / n) <= (t + X) / t)
+ 				break;
+ 		}
+ 		*stateptr = W;
+ 	}
+ 	return S;
+ }
+ 
+ /* ---------------------------------------------------
+  * Random number generator
+  * ---------------------------------------------------
+  */
+ 
+ /* Select a random value R uniformly distributed in (0 - 1), for tablsample use */
+ double
+ anl_random_fract(void *state)
+ {
+ 	/* XXX: If the sample_random can work as good as random(), may merge,
+ 	 * but now, as sample_random surely needs improvement, we leave 
+ 	 * analzye.c and tablesample using different random generator.
+ 	 */
+ 	if(state == NULL)
+ 	{
+ 		return ((double) random() + 1) / ((double) MAX_RANDOM_VALUE + 2);
+ 	}else{
+ 		return sample_random(state);
+ 	}
+ }
+ 
+ /*
+  * Returns a randomly-generated trigger x, such that a <= x < b
+  */
+ int
+ get_rand_in_range(void *rand_state, int a, int b)
+ {
+ 	double rand = sample_random(rand_state);
+ 	return (rand * b) + a;
+ }
+ 
+ /*
+  * Random Number Seeding
+  */
+ void sample_set_seed(void *random_state, double seed)
+ {
+ 	/*     
+ 	 * XXX. This seeding algorithm could certainly be improved.
+ 	 * May need some effort on this.
+ 	 */    
+ 	seed += MEM_OVERHEAD; //seed must be large enough to make difference
+ 
+ 	memset(random_state, 0, sizeof(random_state));
+ 	memcpy(random_state,
+ 			&seed,
+ 			Min(sizeof(random_state), sizeof(seed)));
+ }
+ 
+ double sample_random(void *random_state)
+ {
+ 	return pg_erand48(random_state);
+ }
*** a/src/include/access/relscan.h
--- b/src/include/access/relscan.h
***************
*** 51,56 **** typedef struct HeapScanDescData
--- 51,69 ----
  	int			rs_mindex;		/* marked tuple's saved index */
  	int			rs_ntuples;		/* number of visible tuples on page */
  	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
+ 
+ 	/* The random number generator state */
+ 	unsigned short rs_randstate[3];
+ 
+ 	/* TableSample Bernoulli Vitter's Algo samples */
+ 	bool		is_sample_scan;
+ 	int			sample_percent;
+ 	TableSampleMethod sample_method;
+ 	HeapTuple	*rs_samplerows;
+ 	int			rs_samplesize;
+ 	int			rs_curindex;
+ 	bool		rs_sampleinited;
+ 	int			targrows;
  }	HeapScanDescData;
  
  /*
*** a/src/include/commands/vacuum.h
--- b/src/include/commands/vacuum.h
***************
*** 170,177 **** extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
  extern void analyze_rel(Oid relid, VacuumStmt *vacstmt,
  			BufferAccessStrategy bstrategy);
  extern bool std_typanalyze(VacAttrStats *stats);
- extern double anl_random_fract(void);
- extern double anl_init_selection_state(int n);
- extern double anl_get_next_S(double t, int n, double *stateptr);
  
  #endif   /* VACUUM_H */
--- 170,174 ----
*** /dev/null
--- b/src/include/executor/nodeSamplescan.h
***************
*** 0 ****
--- 1,28 ----
+ /*-------------------------------------------------------------------------
+  *
+  * nodeSamplescan.h
+  *
+  *
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/executor/nodeSamplescan.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef NODESAMPLESCAN_H
+ #define NODESAMPLESCAN_H
+ 
+ #include "nodes/execnodes.h"
+ 
+ #define RAND_STATE_SIZE 128
+ 
+ extern SampleScanState *ExecInitSampleScan(SampleScan *node, EState *estate, int eflags);
+ extern TupleTableSlot *ExecSampleScan(SampleScanState *node);
+ extern void ExecEndSampleScan(SampleScanState *node);
+ extern void ExecSampleMarkPos(SampleScanState *node);
+ extern void ExecSampleRestrPos(SampleScanState *node);
+ extern void ExecReScanSampleScan(SampleScanState *node);
+ 
+ #endif   /* NODESAMPLESCAN_H */
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 1430,1435 **** typedef struct ValuesScanState
--- 1430,1455 ----
  	int			marked_idx;
  } ValuesScanState;
  
+ /* --------------
+  * This State struct should be decided by the algorithm used in Sampling
+  * method.
+  *
+  * SampleScanState: the run-time state associated with a single sample
+  * scan. This is the run-time dual of the SampleScan plan node: for
+  * each SampleScan in the Plan tree, we create a SampleScanState in
+  * the corresponding PlanState tree. A PlanState's associated Plan can
+  * be found via ss.ps.plan.
+  *
+  * --------------
+  */
+ 
+ typedef struct SampleScanState
+ {
+ 	/* parent class; first field is NodeTag */
+ 	ScanState			 ss;
+ } SampleScanState;
+ 
+ 
  /* ----------------
   *	 CteScanState information
   *
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 51,56 **** typedef enum NodeTag
--- 51,57 ----
  	T_BitmapOr,
  	T_Scan,
  	T_SeqScan,
+ 	T_SampleScan,
  	T_IndexScan,
  	T_IndexOnlyScan,
  	T_BitmapIndexScan,
***************
*** 96,101 **** typedef enum NodeTag
--- 97,103 ----
  	T_BitmapOrState,
  	T_ScanState,
  	T_SeqScanState,
+ 	T_SampleScanState,
  	T_IndexScanState,
  	T_IndexOnlyScanState,
  	T_BitmapIndexScanState,
***************
*** 171,176 **** typedef enum NodeTag
--- 173,179 ----
  	T_JoinExpr,
  	T_FromExpr,
  	T_IntoClause,
+ 	T_TableSampleInfo,
  
  	/*
  	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 701,708 **** typedef struct RangeTblEntry
  	/*
  	 * Fields valid for a plain relation RTE (else zero):
  	 */
! 	Oid			relid;			/* OID of the relation */
! 	char		relkind;		/* relation kind (see pg_class.relkind) */
  
  	/*
  	 * Fields valid for a subquery RTE (else NULL):
--- 701,709 ----
  	/*
  	 * Fields valid for a plain relation RTE (else zero):
  	 */
! 	Oid			relid;				/* OID of the relation */
! 	TableSampleInfo *sample_info;	/* Fields for tablesampleinfo */
! 	char		relkind;			/* relation kind (see pg_class.relkind) */
  
  	/*
  	 * Fields valid for a subquery RTE (else NULL):
***************
*** 765,770 **** typedef struct RangeTblEntry
--- 766,772 ----
  	Oid			checkAsUser;	/* if valid, check access as this role */
  	Bitmapset  *selectedCols;	/* columns needing SELECT permission */
  	Bitmapset  *modifiedCols;	/* columns needing INSERT/UPDATE permission */
+ 
  } RangeTblEntry;
  
  /*
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 271,276 **** typedef struct Scan
--- 271,294 ----
  typedef Scan SeqScan;
  
  /* ----------------
+  * SampleScan node
+  *
+  * This is the information about a SampleScan that is fixed for a given Plan.
+  * SampleScanState holds the run-time(executor-time) state associated with a
+  * given ScanScan node.
+  *
+  * In addition to our parent class, we need only a single additional piece of
+  * information: the information contained in the TABLESAMPLE clause that corresponds
+  * to this SampleScan.
+  * ----------------
+  */
+ typedef struct SampleScan
+ {
+ 	Scan				scan;
+ 	TableSampleInfo	   *sample_info;
+ } SampleScan;
+ 
+ /* ----------------
   *		index scan node
   *
   * indexqualorig is an implicitly-ANDed list of index qual expressions, each
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 58,63 **** typedef enum OnCommitAction
--- 58,86 ----
  	ONCOMMIT_DROP				/* ON COMMIT DROP */
  } OnCommitAction;
  
+ /*---------
+  * TableSampleMethod - defines the methods for tablesample query
+  *---------
+  */
+ typedef enum TableSampleMethod
+ {
+ 	SAMPLE_BERNOULLI,
+ 	SAMPLE_SYSTEM
+ } TableSampleMethod;
+ 
+ /*--------
+  * TableSampleInfo - information related to a tablesample query
+  *-------
+  */
+ typedef struct TableSampleInfo
+ {
+ 	NodeTag				type;
+ 	int					sample_percent;
+ 	TableSampleMethod	sample_method;
+ 	bool				is_repeatable;
+ 	int					repeat_seed;
+ } TableSampleInfo;
+ 
  /*
   * RangeVar - range variable, used in FROM clauses
   *
***************
*** 65,70 **** typedef enum OnCommitAction
--- 88,94 ----
   * field is not used, and inhOpt shows whether to apply the operation
   * recursively to child tables.  In some contexts it is also useful to carry
   * a TEMP table indication here.
+  * Also support for TableSample.
   */
  typedef struct RangeVar
  {
***************
*** 77,82 **** typedef struct RangeVar
--- 101,108 ----
  	char		relpersistence; /* see RELPERSISTENCE_* in pg_class.h */
  	Alias	   *alias;			/* table alias & optional column aliases */
  	int			location;		/* token location, or -1 if unknown */
+ 
+ 	TableSampleInfo *sample_info; /* TABLESAMPLE clause, if any */
  } RangeVar;
  
  /*
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 98,103 **** typedef struct PlannerGlobal
--- 98,105 ----
  	Index		lastRowMarkId;	/* highest PlanRowMark ID assigned */
  
  	bool		transientPlan;	/* redo plan when TransactionXmin changes? */
+ 
+ 	bool		isCursorStmt;	/* Is this a cursor stmt? Only for Cursor Tablesample Check */
  } PlannerGlobal;
  
  /* macro for fetching the Plan associated with a SubPlan node */
***************
*** 362,367 **** typedef struct PlannerInfo
--- 364,373 ----
   *					EquivalenceClasses)
   *		has_eclass_joins - flag that EquivalenceClass joins are possible
   *
+  * The next field will be set true if there is tablesample clause presense in
+  * the query, otherwise false;
+  *		has_table_sample - flag for tablesample clause
+  *
   * Note: Keeping a restrictinfo list in the RelOptInfo is useful only for
   * base rels, because for a join rel the set of clauses that are treated as
   * restrict clauses varies depending on which sub-relations we choose to join.
***************
*** 435,440 **** typedef struct RelOptInfo
--- 441,449 ----
  	List	   *joininfo;		/* RestrictInfo structures for join clauses
  								 * involving this rel */
  	bool		has_eclass_joins;		/* T means joininfo is incomplete */
+ 
+ 	/* used if there is tablesample clause */
+ 	bool		has_table_sample;
  } RelOptInfo;
  
  /*
***************
*** 703,708 **** typedef struct Path
--- 712,719 ----
  #define PATH_REQ_OUTER(path)  \
  	((path)->param_info ? (path)->param_info->ppi_req_outer : (Relids) NULL)
  
+ 
+ 
  /*----------
   * IndexPath represents an index scan over a single index.
   *
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
***************
*** 51,56 **** extern PGDLLIMPORT double cpu_operator_cost;
--- 51,57 ----
  extern PGDLLIMPORT int effective_cache_size;
  extern Cost disable_cost;
  extern bool enable_seqscan;
+ extern bool enable_samplescan;
  extern bool enable_indexscan;
  extern bool enable_indexonlyscan;
  extern bool enable_bitmapscan;
***************
*** 84,89 **** extern void cost_functionscan(Path *path, PlannerInfo *root,
--- 85,91 ----
  				  RelOptInfo *baserel, ParamPathInfo *param_info);
  extern void cost_valuesscan(Path *path, PlannerInfo *root,
  				RelOptInfo *baserel, ParamPathInfo *param_info);
+ extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
  extern void cost_ctescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
  extern void cost_recursive_union(Plan *runion, Plan *nrterm, Plan *rterm);
  extern void cost_sort(Path *path, PlannerInfo *root,
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
***************
*** 32,37 **** extern bool add_path_precheck(RelOptInfo *parent_rel,
--- 32,38 ----
  
  extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
  					Relids required_outer);
+ extern Path *create_samplescan_path(PlannerInfo *root, RelOptInfo *rel);
  extern IndexPath *create_index_path(PlannerInfo *root,
  				  IndexOptInfo *index,
  				  List *indexclauses,
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 54,59 **** PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
--- 54,60 ----
  PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
  PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD)
  PG_KEYWORD("begin", BEGIN_P, UNRESERVED_KEYWORD)
+ PG_KEYWORD("bernoulli", BERNOULLI, UNRESERVED_KEYWORD)
  PG_KEYWORD("between", BETWEEN, COL_NAME_KEYWORD)
  PG_KEYWORD("bigint", BIGINT, COL_NAME_KEYWORD)
  PG_KEYWORD("binary", BINARY, TYPE_FUNC_NAME_KEYWORD)
***************
*** 358,363 **** PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD)
--- 359,365 ----
  PG_KEYWORD("system", SYSTEM_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("table", TABLE, RESERVED_KEYWORD)
  PG_KEYWORD("tables", TABLES, UNRESERVED_KEYWORD)
+ PG_KEYWORD("tablesample", TABLESAMPLE, TYPE_FUNC_NAME_KEYWORD)
  PG_KEYWORD("tablespace", TABLESPACE, UNRESERVED_KEYWORD)
  PG_KEYWORD("temp", TEMP, UNRESERVED_KEYWORD)
  PG_KEYWORD("template", TEMPLATE, UNRESERVED_KEYWORD)
*** /dev/null
--- b/src/include/utils/sampleutils.h
***************
*** 0 ****
--- 1,49 ----
+ /*-------------------------------------------------------------------------
+  *
+  * sampleutils.h
+  *	  APIs for Tablesample Support, containing Vitter's Algo APIs, analyze.c
+  *	  is also using the APIs.
+  *
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/utils/sampleutils.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef SAMPLEUTILS_H
+ #define SAMPLEUTILS_H
+ 
+ #include "storage/block.h"
+ #include "utils/relcache.h"
+ #include "access/htup.h"
+ #include "storage/buf.h"
+ 
+ /* Data structure for Algorithm S from Knuth 3.4.2 */
+ typedef struct
+ {
+ 	BlockNumber N;				/* number of blocks, known in advance */
+ 	int			n;				/* desired sample size */
+ 	BlockNumber t;				/* current block number */
+ 	int			m;				/* blocks selected so far */
+ } BlockSamplerData;
+ 
+ typedef BlockSamplerData *BlockSampler;
+ 
+ 
+ extern void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
+ 				  int samplesize);
+ extern bool BlockSampler_HasMore(BlockSampler bs);
+ extern BlockNumber BlockSampler_Next(void *state, BlockSampler bs);
+ extern double anl_init_selection_state(void *state, int n);
+ extern double anl_get_next_S(void *state, double t, int n, double *stateptr);
+ extern double anl_random_fract(void *state);
+ extern int get_rand_in_range(void *state, int a, int b);
+ extern void sample_set_seed(void *random_state, double seed);
+ extern double sample_random(void *random_state);
+ extern int acquire_vitter_rows(Relation onerel, void *state, HeapTuple *rows, int targrows,
+ 							BlockNumber totalblocks, BufferAccessStrategy vac_strategy,
+ 							double *totalrows, double *totaldeadrows, bool isanalyze, int elevel);
+ 
+ #endif /* SAMPLEUTILS_H */
*** /dev/null
--- b/src/test/regress/expected/tablesample.out
***************
*** 0 ****
--- 1,188 ----
+ --
+ -- TABLESAMPLE TEST
+ --
+ CREATE TABLE sample (id INT, name text);
+ INSERT INTO sample VALUES (1, 'victor');
+ INSERT INTO sample VALUES (2, 'peter');
+ INSERT INTO sample VALUES (3, 'james');
+ INSERT INTO sample VALUES (4, 'paul');
+ INSERT INTO sample VALUES (5, 'kate');
+ INSERT INTO sample VALUES (6, 'jane');
+ INSERT INTO sample VALUES (7, 'samual');
+ INSERT INTO sample VALUES (8, 'david');
+ INSERT INTO sample VALUES (9, 'job');
+ INSERT INTO sample VALUES (10, 'neil');
+ INSERT INTO sample VALUES (11, 'victor');
+ INSERT INTO sample VALUES (12, 'peter');
+ INSERT INTO sample VALUES (13, 'james');
+ INSERT INTO sample VALUES (14, 'paul');
+ INSERT INTO sample VALUES (15, 'kate');
+ INSERT INTO sample VALUES (16, 'jane');
+ INSERT INTO sample VALUES (17, 'samual');
+ INSERT INTO sample VALUES (18, 'david');
+ INSERT INTO sample VALUES (19, 'job');
+ INSERT INTO sample VALUES (20, 'neil');
+ ANALYZE sample;
+ SELECT * FROM sample TABLESAMPLE SYSTEM (60) REPEATABLE (10);
+  id |  name  
+ ----+--------
+   1 | victor
+   2 | peter
+   3 | james
+   4 | paul
+   5 | kate
+   6 | jane
+   7 | samual
+   8 | david
+   9 | job
+  10 | neil
+  11 | victor
+  12 | peter
+  13 | james
+  14 | paul
+  15 | kate
+  16 | jane
+  17 | samual
+  18 | david
+  19 | job
+  20 | neil
+ (20 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (11);
+  id |  name  
+ ----+--------
+  18 | david
+   2 | peter
+   3 | james
+  15 | kate
+   5 | kate
+   6 | jane
+   7 | samual
+  20 | neil
+   9 | job
+  10 | neil
+  11 | victor
+  12 | peter
+  16 | jane
+  17 | samual
+ (14 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (11);
+  id |  name  
+ ----+--------
+  18 | david
+   2 | peter
+   3 | james
+  15 | kate
+   5 | kate
+   6 | jane
+   7 | samual
+  20 | neil
+   9 | job
+  10 | neil
+  11 | victor
+  12 | peter
+  16 | jane
+  17 | samual
+ (14 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (19);
+  id |  name  
+ ----+--------
+   1 | victor
+  18 | david
+   3 | james
+  17 | samual
+   5 | kate
+   6 | jane
+   7 | samual
+   8 | david
+  15 | kate
+  19 | job
+  11 | victor
+  12 | peter
+  13 | james
+  14 | paul
+ (14 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (7) REPEATABLE (19);
+  id |  name  
+ ----+--------
+   7 | samual
+ (1 row)
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (1) REPEATABLE (19);
+  id | name 
+ ----+------
+ (0 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (65) REPEATABLE (20);
+  id |  name  
+ ----+--------
+   1 | victor
+   2 | peter
+   3 | james
+   4 | paul
+   5 | kate
+   6 | jane
+   7 | samual
+   8 | david
+   9 | job
+  10 | neil
+  11 | victor
+  12 | peter
+  13 | james
+  14 | paul
+  15 | kate
+  16 | jane
+  17 | samual
+  18 | david
+  19 | job
+  20 | neil
+ (20 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (65) REPEATABLE (2);
+  id | name 
+ ----+------
+ (0 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (65) REPEATABLE (20);
+  id |  name  
+ ----+--------
+   1 | victor
+   2 | peter
+   3 | james
+   4 | paul
+   5 | kate
+   6 | jane
+   7 | samual
+   8 | david
+   9 | job
+  10 | neil
+  11 | victor
+  12 | peter
+  13 | james
+  14 | paul
+  15 | kate
+  16 | jane
+  17 | samual
+  18 | david
+  19 | job
+  20 | neil
+ (20 rows)
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (11) order by id limit 5;
+  id |  name  
+ ----+--------
+   2 | peter
+   3 | james
+   5 | kate
+   6 | jane
+   7 | samual
+ (5 rows)
+ 
+ SELECT * INTO tmp FROM sample TABLESAMPLE BERNOULLI (4);
+ DROP TABLE tmp;
+ SELECT * INTO tmp FROM sample TABLESAMPLE SYSTEM (20);
+ DROP TABLE tmp;
+ DROP TABLE sample;
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 59,65 **** test: create_index create_view
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 59,65 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists tablesample
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 67,72 **** test: create_table_like
--- 67,73 ----
  test: typed_table
  test: vacuum
  test: drop_if_exists
+ test: tablesample
  test: sanity_check
  test: errors
  test: select
*** /dev/null
--- b/src/test/regress/sql/tablesample.sql
***************
*** 0 ****
--- 1,58 ----
+ --
+ -- TABLESAMPLE TEST
+ --
+ 
+ CREATE TABLE sample (id INT, name text);
+ 
+ INSERT INTO sample VALUES (1, 'victor');
+ INSERT INTO sample VALUES (2, 'peter');
+ INSERT INTO sample VALUES (3, 'james');
+ INSERT INTO sample VALUES (4, 'paul');
+ INSERT INTO sample VALUES (5, 'kate');
+ INSERT INTO sample VALUES (6, 'jane');
+ INSERT INTO sample VALUES (7, 'samual');
+ INSERT INTO sample VALUES (8, 'david');
+ INSERT INTO sample VALUES (9, 'job');
+ INSERT INTO sample VALUES (10, 'neil');
+ INSERT INTO sample VALUES (11, 'victor');
+ INSERT INTO sample VALUES (12, 'peter');
+ INSERT INTO sample VALUES (13, 'james');
+ INSERT INTO sample VALUES (14, 'paul');
+ INSERT INTO sample VALUES (15, 'kate');
+ INSERT INTO sample VALUES (16, 'jane');
+ INSERT INTO sample VALUES (17, 'samual');
+ INSERT INTO sample VALUES (18, 'david');
+ INSERT INTO sample VALUES (19, 'job');
+ INSERT INTO sample VALUES (20, 'neil');
+ 
+ ANALYZE sample;
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (60) REPEATABLE (10);
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (11);
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (11);
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (19);
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (7) REPEATABLE (19);
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (1) REPEATABLE (19);
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (65) REPEATABLE (20);
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (65) REPEATABLE (2);
+ 
+ SELECT * FROM sample TABLESAMPLE SYSTEM (65) REPEATABLE (20);
+ 
+ SELECT * FROM sample TABLESAMPLE BERNOULLI (70) REPEATABLE (11) order by id limit 5;
+ 
+ SELECT * INTO tmp FROM sample TABLESAMPLE BERNOULLI (4);
+ 
+ DROP TABLE tmp;
+ 
+ SELECT * INTO tmp FROM sample TABLESAMPLE SYSTEM (20);
+ 
+ DROP TABLE tmp;
+ 
+ DROP TABLE sample;