Index: doc/src/sgml/errcodes.sgml
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/doc/src/sgml/errcodes.sgml,v
retrieving revision 1.22
diff -p -c -r1.22 errcodes.sgml
*** doc/src/sgml/errcodes.sgml	31 Jan 2007 20:56:17 -0000	1.22
--- doc/src/sgml/errcodes.sgml	17 May 2007 23:36:59 -0000
***************
*** 440,445 ****
--- 440,451 ----
  </row>
  
  <row>
+ <entry><literal>2202H</literal></entry>
+ <entry>INVALID_SAMPLE_SIZE</entry>
+ <entry>invalid_sample_size</entry>
+ </row>
+ 
+ <row>
  <entry><literal>22009</literal></entry>
  <entry>INVALID TIME ZONE DISPLACEMENT VALUE</entry>
  <entry>invalid_time_zone_displacement_value</entry>
Index: doc/src/sgml/ref/select.sgml
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/doc/src/sgml/ref/select.sgml,v
retrieving revision 1.100
diff -p -c -r1.100 select.sgml
*** doc/src/sgml/ref/select.sgml	15 May 2007 19:13:55 -0000	1.100
--- doc/src/sgml/ref/select.sgml	17 May 2007 23:36:59 -0000
*************** SELECT [ ALL | DISTINCT [ ON ( <replacea
*** 34,40 ****
  
  where <replaceable class="parameter">from_item</replaceable> can be one of:
  
!     [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
      ( <replaceable class="parameter">select</replaceable> ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ]
      <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] | <replaceable class="parameter">column_definition</replaceable> [, ...] ) ]
      <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) AS ( <replaceable class="parameter">column_definition</replaceable> [, ...] )
--- 34,40 ----
  
  where <replaceable class="parameter">from_item</replaceable> can be one of:
  
!     [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ] [ TABLESAMPLE { BERNOULLI | SYSTEM } ( <replaceable class="parameter">sample_percent</replaceable> ) [ REPEATABLE ( <replaceable class="parameter">repeat_seed</replaceable> ) ] ]
      ( <replaceable class="parameter">select</replaceable> ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ]
      <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] | <replaceable class="parameter">column_definition</replaceable> [, ...] ) ]
      <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) AS ( <replaceable class="parameter">column_definition</replaceable> [, ...] )
*************** where <replaceable class="parameter">fro
*** 215,221 ****
         </para>
        </listitem>
       </varlistentry>
!      
       <varlistentry>
        <term><replaceable class="parameter">select</replaceable></term>
        <listitem>
--- 215,258 ----
         </para>
        </listitem>
       </varlistentry>
! 
!      <varlistentry>
!       <indexterm>
!        <primary>TABLESAMPLE</primary>
!       </indexterm>
!       <term><literal>TABLESAMPLE { BERNOULLI | SYSTEM } ( <replaceable class="parameter">sample_percent</replaceable> ) [ REPEATABLE ( <replaceable class="parameter">repeat_seed</replaceable> ) ]</literal></term>
!       <listitem>
!        <para>
!         Indicates that a table should be randomly sampled from, rather
!         than exhaustively scanned. The scan will visit approximately
!         <replaceable class="parameter">sample_percent</> percent of
!         the blocks of the table. In each sampled block, all the rows
!         will be returned. If the number of visible rows in every block
!         is approximately equal, this will result in sampling about
!         <replaceable class="parameter">sample_percent</replaceable>
!         percent of the rows in the table.
!        </para>
! 
!        <para>
!         In <productname>PostgreSQL</productname> 8.3, only the
!         <literal>SYSTEM</literal> sample method is supported.
!        </para>
! 
!        <para>
!         The <literal>REPEATABLE</literal> clause can be used to
!         produce consistent results from a probabilistic table
!         scan. The <replaceable class="parameter">repeat_seed</> is
!         used to seed the random number generator before sampling
!         begins. Therefore, the same sequence of numbers will be
!         generated whenever the same <literal>REPEATABLE</literal>
!         clause is specified. Note that by itself, this is not
!         sufficient to ensure that the sampling query will produce the
!         same results: the query's snapshot could have changed, or the
!         query might contain non-deterministic elements.
!        </para>
!       </listitem>
!      </varlistentry>
! 
       <varlistentry>
        <term><replaceable class="parameter">select</replaceable></term>
        <listitem>
Index: src/backend/commands/explain.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.163
diff -p -c -r1.163 explain.c
*** src/backend/commands/explain.c	4 May 2007 21:29:52 -0000	1.163
--- src/backend/commands/explain.c	17 May 2007 23:36:59 -0000
*************** explain_outNode(StringInfo str,
*** 525,530 ****
--- 525,533 ----
  		case T_ValuesScan:
  			pname = "Values Scan";
  			break;
+ 		case T_SampleScan:
+ 			pname = "Sample Scan";
+ 			break;
  		case T_Material:
  			pname = "Materialize";
  			break;
*************** explain_outNode(StringInfo str,
*** 597,602 ****
--- 600,606 ----
  		case T_SeqScan:
  		case T_BitmapHeapScan:
  		case T_TidScan:
+ 		case T_SampleScan:
  			if (((Scan *) plan)->scanrelid > 0)
  			{
  				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
*************** explain_outNode(StringInfo str,
*** 743,748 ****
--- 747,753 ----
  		case T_SeqScan:
  		case T_FunctionScan:
  		case T_ValuesScan:
+ 		case T_SampleScan:
  			show_scan_qual(plan->qual,
  						   "Filter",
  						   ((Scan *) plan)->scanrelid,
Index: src/backend/executor/Makefile
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/executor/Makefile,v
retrieving revision 1.25
diff -p -c -r1.25 Makefile
*** src/backend/executor/Makefile	20 Jan 2007 17:16:11 -0000	1.25
--- src/backend/executor/Makefile	17 May 2007 23:36:59 -0000
*************** OBJS = execAmi.o execGrouping.o execJunk
*** 18,25 ****
         nodeBitmapAnd.o nodeBitmapOr.o \
         nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
         nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
!        nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
!        nodeSetOp.o nodeSort.o nodeUnique.o \
         nodeValuesscan.o nodeLimit.o nodeGroup.o \
         nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
  
--- 18,25 ----
         nodeBitmapAnd.o nodeBitmapOr.o \
         nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
         nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
!        nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSamplescan.o \
!        nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
         nodeValuesscan.o nodeLimit.o nodeGroup.o \
         nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
  
Index: src/backend/executor/execAmi.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/executor/execAmi.c,v
retrieving revision 1.92
diff -p -c -r1.92 execAmi.c
*** src/backend/executor/execAmi.c	19 Feb 2007 02:23:11 -0000	1.92
--- src/backend/executor/execAmi.c	17 May 2007 23:36:59 -0000
*************** ExecMayReturnRawTuples(PlanState *node)
*** 440,445 ****
--- 440,446 ----
  	switch (nodeTag(node))
  	{
  			/* Table scan nodes */
+ 		case T_SampleScanState:
  		case T_SeqScanState:
  		case T_IndexScanState:
  		case T_BitmapHeapScanState:
Index: src/backend/executor/execProcnode.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/executor/execProcnode.c,v
retrieving revision 1.61
diff -p -c -r1.61 execProcnode.c
*** src/backend/executor/execProcnode.c	27 Feb 2007 01:11:25 -0000	1.61
--- src/backend/executor/execProcnode.c	17 May 2007 23:36:59 -0000
***************
*** 95,100 ****
--- 95,101 ----
  #include "executor/nodeMergejoin.h"
  #include "executor/nodeNestloop.h"
  #include "executor/nodeResult.h"
+ #include "executor/nodeSamplescan.h"
  #include "executor/nodeSeqscan.h"
  #include "executor/nodeSetOp.h"
  #include "executor/nodeSort.h"
*************** ExecInitNode(Plan *node, EState *estate,
*** 200,205 ****
--- 201,211 ----
  													  estate, eflags);
  			break;
  
+ 		case T_SampleScan:
+ 			result = (PlanState *) ExecInitSampleScan((SampleScan *) node,
+ 													  estate, eflags);
+ 			break;
+ 
  			/*
  			 * join nodes
  			 */
*************** ExecProcNode(PlanState *node)
*** 360,365 ****
--- 366,375 ----
  			result = ExecValuesScan((ValuesScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			result = ExecSampleScan((SampleScanState *) node);
+ 			break;
+ 
  			/*
  			 * join nodes
  			 */
*************** ExecCountSlotsNode(Plan *node)
*** 534,539 ****
--- 544,552 ----
  		case T_ValuesScan:
  			return ExecCountSlotsValuesScan((ValuesScan *) node);
  
+ 		case T_SampleScan:
+ 			return ExecCountSlotsSampleScan((SampleScan *) node);
+ 
  			/*
  			 * join nodes
  			 */
*************** ExecEndNode(PlanState *node)
*** 663,668 ****
--- 676,685 ----
  			ExecEndValuesScan((ValuesScanState *) node);
  			break;
  
+ 		case T_SampleScanState:
+ 			ExecEndSampleScan((SampleScanState *) node);
+ 			break;
+ 
  			/*
  			 * join nodes
  			 */
Index: src/backend/executor/nodeSamplescan.c
===================================================================
RCS file: src/backend/executor/nodeSamplescan.c
diff -N src/backend/executor/nodeSamplescan.c
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- src/backend/executor/nodeSamplescan.c	21 May 2007 18:13:20 -0000
***************
*** 0 ****
--- 1,347 ----
+ /*-------------------------------------------------------------------------
+  *
+  * nodeSamplescan.c
+  *	  Support routines for TABLESAMPLE-based scans of a relation
+  *
+  * Copyright (c) 2007, PostgreSQL Global Development Group
+  *
+  *
+  * IDENTIFICATION
+  *	  $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include <time.h>
+ 
+ #include "access/heapam.h"
+ #include "executor/executor.h"
+ #include "executor/nodeSamplescan.h"
+ #include "parser/parsetree.h"
+ 
+ static TupleTableSlot 	*SampleGetNext(SampleScanState *node);
+ static void 			 LoadNextSampleBuffer(SampleScanState *node);
+ static int 				 get_rand_in_range(int a, int b);
+ 
+ /*
+  * Initialize the run-time state of the sample scan on a single
+  * relation. This requires setting up various executor machinery and
+  * initializing the state of the PRNG.
+  */
+ SampleScanState *
+ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
+ {
+ 	SampleScanState 	*scanstate;
+ 	Relation 			 rel;
+ 	int 				 seed;
+ 
+ 	/* We don't expect to have any child plan nodes */
+ 	Assert(outerPlan(node) == NULL);
+ 	Assert(innerPlan(node) == NULL);
+ 
+ 	scanstate				= makeNode(SampleScanState);
+ 	scanstate->ss.ps.plan	= (Plan *) node;
+ 	scanstate->ss.ps.state	= estate;
+ 	scanstate->cur_buf		= InvalidBuffer;
+ 	scanstate->cur_offset	= FirstOffsetNumber;
+ 	scanstate->cur_blkno	= InvalidBlockNumber;
+ 	scanstate->need_new_buf = true;
+ 
+ 	ExecAssignExprContext(estate, &scanstate->ss.ps);
+ 
+ 	/*
+ 	 * Initialize the expression contexts required for evaluating the
+ 	 * target list and the scan's qualifiers, if any. We don't need to
+ 	 * do qual evaluation ourselves (ExecScan does it), but we do need
+ 	 * to do the required initialization.
+ 	 */
+ 	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);
+ 
+ #define SAMPLESCAN_NSLOTS 2
+ 
+ 	/*
+ 	 * Initialize the tuple table slots required by this scan: we need a
+ 	 * slot for the current result of the scan, and a slot for the
+ 	 * current scan tuple.
+ 	 */
+ 	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+ 	ExecInitScanTupleSlot(estate, &scanstate->ss);
+ 
+ 	/*
+ 	 * Open and lock the heap relation we're going to scan.
+ 	 * ExecOpenScanRelation() will acquire the appropriate lock,
+ 	 * depending on whether we're scanning this table with FOR UPDATE,
+ 	 * FOR SHARE, or in normal mode.
+ 	 */
+ 	rel = ExecOpenScanRelation(estate, node->scan.scanrelid);
+ 	scanstate->ss.ss_currentRelation = rel;
+ 
+ 	/*
+ 	 * Determine the number of blocks in the relation. We need only do
+ 	 * this once for a given scan: if any new blocks are added to the
+ 	 * relation, they won't be visible to this transaction anyway.
+ 	 */
+ 	scanstate->nblocks = RelationGetNumberOfBlocks(rel);
+ 
+ 	ExecAssignScanType(&scanstate->ss, RelationGetDescr(rel));
+ 
+ 	scanstate->ss.ps.ps_TupFromTlist = false;
+ 
+ 	/* Initialize result tuple type and projection info */
+ 	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+ 	ExecAssignScanProjectionInfo(&scanstate->ss);
+ 
+ 	/*
+ 	 * 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 = (int) time(NULL);
+ 
+ 	scanstate->prev_rand_state = initstate(seed, scanstate->rand_state,
+ 										   sizeof(scanstate->rand_state));
+ 	setstate(scanstate->prev_rand_state);
+ 
+ 	return scanstate;
+ }
+ 
+ /* Return the next tuple in the sample scan's result set. */
+ TupleTableSlot *
+ ExecSampleScan(SampleScanState *node)
+ {
+ 	char *prev_state;
+ 
+ 	PG_TRY();
+ 	{
+ 		/* Install our PRNG state */
+ 		prev_state = setstate(node->rand_state);
+ 
+ 		/*
+ 		 * ExecScan() provides generic infrastructure for "scan-like"
+ 		 * executor nodes. It takes a ScanState describing the scan
+ 		 * and a function pointer to an "access method". The access
+ 		 * method is invoked repeatedly by ExecScan(); for each call,
+ 		 * the access method should return the next tuple produced by
+ 		 * the scan. ExecScan() then handles checking any relevant
+ 		 * scan qualifiers, performing projection if necessary, and
+ 		 * then stashing the result tuple in the appropriate
+ 		 * TupleTableSlot.
+ 		 */
+ 		return ExecScan((ScanState *) node,
+ 						(ExecScanAccessMtd) SampleGetNext);
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		setstate(prev_state);
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	setstate(prev_state);
+ }
+ 
+ static TupleTableSlot *
+ SampleGetNext(SampleScanState *node)
+ {
+ 	EState	   			*estate;
+ 	TupleTableSlot 		*slot;
+ 	Relation			 rel;
+ 	Index				 scanrelid;
+ 
+ 	estate 	  = node->ss.ps.state;
+ 	slot 	  = node->ss.ss_ScanTupleSlot;
+ 	rel 	  = node->ss.ss_currentRelation;
+ 	scanrelid = ((SampleScan *) node->ss.ps.plan)->scan.scanrelid;
+ 
+ 	while (true)
+ 	{
+ 		OffsetNumber 	max_offset;
+ 		Page 			page;
+ 
+ 		/*
+ 		 * If we don't have a valid buffer, choose the next block to
+ 		 * sample and load it into memory.
+ 		 */
+ 		if (node->need_new_buf)
+ 		{
+ 			LoadNextSampleBuffer(node);
+ 			node->need_new_buf = false;
+ 
+ 			/* We're out of blocks in the rel, so we're done */
+ 			if (!BufferIsValid(node->cur_buf))
+ 				break;
+ 		}
+ 
+ 		/*
+ 		 * Iterate through the current block, checking for heap tuples
+ 		 * that are visible to our transaction. Return each such
+ 		 * candidate match: ExecScan() takes care of checking whether
+ 		 * the tuple satisfies the scan's quals.
+ 		 */
+ 		LockBuffer(node->cur_buf, BUFFER_LOCK_SHARE);
+ 		page = BufferGetPage(node->cur_buf);
+ 		max_offset = PageGetMaxOffsetNumber(page);
+ 		while (node->cur_offset <= max_offset)
+ 		{
+ 			/*
+ 			 * Postgres uses a somewhat unusual API for specifying the
+ 			 * location of the tuple we want to fetch. We've already
+ 			 * allocated space for a HeapTupleData; to indicate the TID
+ 			 * we want to fetch into the HeapTuple, we fillin its
+ 			 * "t_self" field, and then ask the heap access manager to
+ 			 * fetch the tuple's data for us.
+ 			 */
+ 			ItemPointerSet(&node->cur_tup.t_self,
+ 						   node->cur_blkno, node->cur_offset);
+ 
+ 			node->cur_offset++;
+ 
+ 			if (heap_release_fetch(rel, estate->es_snapshot,
+ 								   &node->cur_tup, &node->cur_buf,
+ 								   true, NULL))
+ 			{
+ 				LockBuffer(node->cur_buf, BUFFER_LOCK_UNLOCK);
+ 
+ 				ExecStoreTuple(&node->cur_tup,
+ 							   slot,
+ 							   node->cur_buf,
+ 							   false);
+ 
+ 				return slot;
+ 			}
+ 		}
+ 
+ 		/* Out of tuples on this page, so go on to the next one */
+ 		LockBuffer(node->cur_buf, BUFFER_LOCK_UNLOCK);
+ 		node->need_new_buf = true;
+ 	}
+ 
+ 	/* No more blocks to scan, so we're done: clear result slot */
+ 	ExecClearTuple(slot);
+ 	return NULL;
+ }
+ 
+ /*
+  * Choose the next block from the relation to sample. This is called
+  * when (a) we haven't sampled any blocks from the relation yet
+  * (SampleScanState.cur_buf == InvalidBuffer) (b) we've examined every
+  * tuple in the block we're currently sampling.
+  *
+  * If we've run out of blocks in the relation, we leave "cur_buf" as
+  * InvalidBuffer.
+  */
+ static void
+ LoadNextSampleBuffer(SampleScanState *node)
+ {
+ 	SampleScan *plan_node = (SampleScan *) node->ss.ps.plan;
+ 
+ 	while (true)
+ 	{
+ 		int rand_percent;
+ 
+ 		/*
+ 		 * If this is the first time through, start at the beginning of
+ 		 * the heap.
+ 		 */
+ 		if (BlockNumberIsValid(node->cur_blkno))
+ 			node->cur_blkno++;
+ 		else
+ 			node->cur_blkno = 0;
+ 
+ 		rand_percent = get_rand_in_range(0, 100);
+ 
+ 		if (rand_percent >= plan_node->sample_info->sample_percent)
+ 			continue;
+ 
+ 		/*
+ 		 * If we've reached the end of the heap, we're done. Make sure
+ 		 * to unpin the current buffer, if any.
+ 		 */
+ 		if (node->cur_blkno >= node->nblocks)
+ 		{
+ 			if (BufferIsValid(node->cur_buf))
+ 			{
+ 				ReleaseBuffer(node->cur_buf);
+ 				node->cur_buf = InvalidBuffer;
+ 			}
+ 
+ 			break;
+ 		}
+ 
+ 		/*
+ 		 * Okay, we've chosen another block to read: ask the bufmgr to
+ 		 * load it into the buffer pool for us, pin it, and release the
+ 		 * pin we hold on the previous "cur_buf". For the case that
+ 		 * "cur_buf" == InvalidBuffer, ReleaseAndReadBuffer() is
+ 		 * equivalent to ReadBuffer().
+ 		 */
+ 		node->cur_buf = ReleaseAndReadBuffer(node->cur_buf,
+ 											 node->ss.ss_currentRelation,
+ 											 node->cur_blkno);
+ 		node->cur_offset = FirstOffsetNumber;
+ 		break;
+ 	}
+ }
+ 
+ /* Returns a randomly-generated integer x, such that a <= x < b. */
+ static int
+ get_rand_in_range(int a, int b)
+ {
+ 	/*
+ 	 * XXX: Using modulus takes the low-order bits of the random
+ 	 * number; since the high-order bits may contain more entropy with
+ 	 * some PRNGs, we should probably use those instead.
+ 	 */
+ 	return (random() % b) + a;
+ }
+ 
+ /*
+  * Count the number of tuple table slots required by an instance of
+  * the SampleScan.
+  */
+ int
+ ExecCountSlotsSampleScan(SampleScan *node)
+ {
+ 	return SAMPLESCAN_NSLOTS;
+ }
+ 
+ /*
+  * Shutdown this scan. This function should generally be symmetric with
+  * ExecInitSampleScan(): we ought to clean up after ourselves.
+  */
+ void
+ ExecEndSampleScan(SampleScanState *node)
+ {
+ 	setstate(node->prev_rand_state);
+ 
+ 	ExecFreeExprContext(&node->ss.ps);
+ 
+ 	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+ 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+ 
+ 	if (BufferIsValid(node->cur_buf))
+ 	{
+ 		ReleaseBuffer(node->cur_buf);
+ 		node->cur_buf = InvalidBuffer;
+ 	}
+ 
+ 	/*
+ 	 * 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);
+ }
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.375
diff -p -c -r1.375 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	27 Apr 2007 22:05:47 -0000	1.375
--- src/backend/nodes/copyfuncs.c	17 May 2007 23:36:59 -0000
*************** _copyValuesScan(ValuesScan *from)
*** 420,425 ****
--- 420,441 ----
  }
  
  /*
+  * _copySampleScan
+  */
+ static SampleScan *
+ _copySampleScan(SampleScan *from)
+ {
+ 	SampleScan *newnode = makeNode(SampleScan);
+ 
+ 	/* copy node superclass fields */
+ 	CopyScanFields((Scan *) from, (Scan *) newnode);
+ 
+ 	COPY_NODE_FIELD(sample_info);
+ 
+ 	return newnode;
+ }
+ 
+ /*
   * CopyJoinFields
   *
   *		This function copies the fields of the Join node.  It is used by
*************** _copyRangeVar(RangeVar *from)
*** 718,723 ****
--- 734,740 ----
  	COPY_SCALAR_FIELD(inhOpt);
  	COPY_SCALAR_FIELD(istemp);
  	COPY_NODE_FIELD(alias);
+ 	COPY_NODE_FIELD(sample_info);
  
  	return newnode;
  }
*************** _copyFromExpr(FromExpr *from)
*** 1350,1355 ****
--- 1367,1388 ----
  	return newnode;
  }
  
+ /*
+  * _copyTableSampleInfo
+  */
+ static TableSampleInfo *
+ _copyTableSampleInfo(TableSampleInfo *from)
+ {
+ 	TableSampleInfo *newnode = makeNode(TableSampleInfo);
+ 
+ 	COPY_SCALAR_FIELD(sample_method);
+ 	COPY_SCALAR_FIELD(sample_percent);
+ 	COPY_SCALAR_FIELD(is_repeatable);
+ 	COPY_SCALAR_FIELD(repeat_seed);
+ 
+ 	return newnode;
+ }
+ 
  /* ****************************************************************
   *						relation.h copy functions
   *
*************** _copyRangeTblEntry(RangeTblEntry *from)
*** 1476,1481 ****
--- 1509,1515 ----
  
  	COPY_SCALAR_FIELD(rtekind);
  	COPY_SCALAR_FIELD(relid);
+ 	COPY_NODE_FIELD(sample_info);
  	COPY_NODE_FIELD(subquery);
  	COPY_NODE_FIELD(funcexpr);
  	COPY_NODE_FIELD(funccoltypes);
*************** copyObject(void *from)
*** 3013,3018 ****
--- 3047,3055 ----
  		case T_ValuesScan:
  			retval = _copyValuesScan(from);
  			break;
+ 		case T_SampleScan:
+ 			retval = _copySampleScan(from);
+ 			break;
  		case T_Join:
  			retval = _copyJoin(from);
  			break;
*************** copyObject(void *from)
*** 3170,3175 ****
--- 3207,3215 ----
  		case T_FromExpr:
  			retval = _copyFromExpr(from);
  			break;
+ 		case T_TableSampleInfo:
+ 			retval = _copyTableSampleInfo(from);
+ 			break;
  
  			/*
  			 * RELATION NODES
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.306
diff -p -c -r1.306 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	27 Apr 2007 22:05:47 -0000	1.306
--- src/backend/nodes/equalfuncs.c	17 May 2007 23:36:59 -0000
*************** _equalRangeVar(RangeVar *a, RangeVar *b)
*** 98,103 ****
--- 98,104 ----
  	COMPARE_SCALAR_FIELD(inhOpt);
  	COMPARE_SCALAR_FIELD(istemp);
  	COMPARE_NODE_FIELD(alias);
+ 	COMPARE_NODE_FIELD(sample_info);
  
  	return true;
  }
*************** _equalFromExpr(FromExpr *a, FromExpr *b)
*** 626,631 ****
--- 627,643 ----
  	return true;
  }
  
+ static bool
+ _equalTableSampleInfo(TableSampleInfo *a, TableSampleInfo *b)
+ {
+ 	COMPARE_SCALAR_FIELD(sample_method);
+ 	COMPARE_SCALAR_FIELD(sample_percent);
+ 	COMPARE_SCALAR_FIELD(is_repeatable);
+ 	COMPARE_SCALAR_FIELD(repeat_seed);
+ 
+ 	return true;
+ }
+ 
  
  /*
   * Stuff from relation.h
*************** _equalRangeTblEntry(RangeTblEntry *a, Ra
*** 1815,1820 ****
--- 1827,1833 ----
  {
  	COMPARE_SCALAR_FIELD(rtekind);
  	COMPARE_SCALAR_FIELD(relid);
+ 	COMPARE_NODE_FIELD(sample_info);
  	COMPARE_NODE_FIELD(subquery);
  	COMPARE_NODE_FIELD(funcexpr);
  	COMPARE_NODE_FIELD(funccoltypes);
*************** equal(void *a, void *b)
*** 2114,2119 ****
--- 2127,2135 ----
  		case T_JoinExpr:
  			retval = _equalJoinExpr(a, b);
  			break;
+ 		case T_TableSampleInfo:
+ 			retval = _equalTableSampleInfo(a, b);
+ 			break;
  
  			/*
  			 * RELATION NODES
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.306
diff -p -c -r1.306 outfuncs.c
*** src/backend/nodes/outfuncs.c	27 Apr 2007 22:05:47 -0000	1.306
--- src/backend/nodes/outfuncs.c	17 May 2007 23:36:59 -0000
*************** _outValuesScan(StringInfo str, ValuesSca
*** 445,450 ****
--- 445,460 ----
  }
  
  static void
+ _outSampleScan(StringInfo str, SampleScan *node)
+ {
+ 	WRITE_NODE_TYPE("SAMPLESCAN");
+ 
+ 	_outScanInfo(str, (Scan *) node);
+ 
+ 	WRITE_NODE_FIELD(sample_info);
+ }
+ 
+ static void
  _outJoin(StringInfo str, Join *node)
  {
  	WRITE_NODE_TYPE("JOIN");
*************** _outRangeVar(StringInfo str, RangeVar *n
*** 652,657 ****
--- 662,668 ----
  	WRITE_ENUM_FIELD(inhOpt, InhOption);
  	WRITE_BOOL_FIELD(istemp);
  	WRITE_NODE_FIELD(alias);
+ 	WRITE_NODE_FIELD(sample_info);
  }
  
  static void
*************** _outRelOptInfo(StringInfo str, RelOptInf
*** 1338,1343 ****
--- 1349,1355 ----
  	WRITE_FLOAT_FIELD(tuples, "%.0f");
  	WRITE_NODE_FIELD(subplan);
  	WRITE_NODE_FIELD(subrtable);
+ 	WRITE_BOOL_FIELD(has_table_sample);
  	WRITE_NODE_FIELD(baserestrictinfo);
  	WRITE_NODE_FIELD(joininfo);
  	WRITE_BOOL_FIELD(has_eclass_joins);
*************** _outRangeTblEntry(StringInfo str, RangeT
*** 1768,1773 ****
--- 1780,1788 ----
  	switch (node->rtekind)
  	{
  		case RTE_RELATION:
+ 			WRITE_OID_FIELD(relid);
+ 			WRITE_NODE_FIELD(sample_info);
+ 			break;
  		case RTE_SPECIAL:
  			WRITE_OID_FIELD(relid);
  			break;
*************** _outFkConstraint(StringInfo str, FkConst
*** 2003,2008 ****
--- 2018,2034 ----
  	WRITE_BOOL_FIELD(skip_validation);
  }
  
+ static void
+ _outTableSampleInfo(StringInfo str, TableSampleInfo *node)
+ {
+ 	WRITE_NODE_TYPE("TABLESAMPLE");
+ 
+ 	WRITE_ENUM_FIELD(sample_method, TableSampleMethod);
+ 	WRITE_INT_FIELD(sample_percent);
+ 	WRITE_BOOL_FIELD(is_repeatable);
+ 	WRITE_INT_FIELD(repeat_seed);
+ }
+ 
  
  /*
   * _outNode -
*************** _outNode(StringInfo str, void *obj)
*** 2073,2078 ****
--- 2099,2107 ----
  			case T_ValuesScan:
  				_outValuesScan(str, obj);
  				break;
+ 			case T_SampleScan:
+ 				_outSampleScan(str, obj);
+ 				break;
  			case T_Join:
  				_outJoin(str, obj);
  				break;
*************** _outNode(StringInfo str, void *obj)
*** 2378,2383 ****
--- 2407,2415 ----
  			case T_FkConstraint:
  				_outFkConstraint(str, obj);
  				break;
+ 			case T_TableSampleInfo:
+ 				_outTableSampleInfo(str, obj);
+ 				break;
  			case T_FuncCall:
  				_outFuncCall(str, obj);
  				break;
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/readfuncs.c,v
retrieving revision 1.206
diff -p -c -r1.206 readfuncs.c
*** src/backend/nodes/readfuncs.c	27 Apr 2007 22:05:47 -0000	1.206
--- src/backend/nodes/readfuncs.c	17 May 2007 23:36:59 -0000
*************** _readRangeVar(void)
*** 278,283 ****
--- 278,297 ----
  	READ_ENUM_FIELD(inhOpt, InhOption);
  	READ_BOOL_FIELD(istemp);
  	READ_NODE_FIELD(alias);
+ 	READ_NODE_FIELD(sample_info);
+ 
+ 	READ_DONE();
+ }
+ 
+ static TableSampleInfo *
+ _readTableSampleInfo(void)
+ {
+ 	READ_LOCALS(TableSampleInfo);
+ 
+ 	READ_ENUM_FIELD(sample_method, TableSampleMethod);
+ 	READ_INT_FIELD(sample_percent);
+ 	READ_BOOL_FIELD(is_repeatable);
+ 	READ_INT_FIELD(repeat_seed);
  
  	READ_DONE();
  }
*************** _readRangeTblEntry(void)
*** 945,950 ****
--- 959,967 ----
  	switch (local_node->rtekind)
  	{
  		case RTE_RELATION:
+ 			READ_OID_FIELD(relid);
+ 			READ_NODE_FIELD(sample_info);
+ 			break;
  		case RTE_SPECIAL:
  			READ_OID_FIELD(relid);
  			break;
*************** parseNodeString(void)
*** 1012,1017 ****
--- 1029,1036 ----
  		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))
Index: src/backend/optimizer/path/allpaths.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/path/allpaths.c,v
retrieving revision 1.163
diff -p -c -r1.163 allpaths.c
*** src/backend/optimizer/path/allpaths.c	21 Apr 2007 21:01:44 -0000	1.163
--- src/backend/optimizer/path/allpaths.c	18 May 2007 06:40:54 -0000
*************** set_plain_rel_pathlist(PlannerInfo *root
*** 238,253 ****
  	 * Note: add_path() will discard any paths that are dominated by another
  	 * available path, keeping only those paths that are superior along at
  	 * least one dimension of cost or sortedness.
  	 */
  
! 	/* Consider sequential scan */
! 	add_path(rel, create_seqscan_path(root, rel));
! 
! 	/* 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);
--- 238,262 ----
  	 * Note: add_path() will discard any paths that are dominated by another
  	 * available path, keeping only those paths that are superior along at
  	 * least one dimension of cost or sortedness.
+ 	 *
+ 	 * If there's a TABLESAMPLE clause, we ONLY consider using a
+ 	 * SampleScan. This could be improved: in some circumstances 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)
+ 		add_path(rel, create_samplescan_path(root, rel));
+ 	else
+ 	{
+ 		/* Consider sequential scan */
+ 		add_path(rel, create_seqscan_path(root, rel));
  
! 		/* 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);
Index: src/backend/optimizer/path/costsize.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/path/costsize.c,v
retrieving revision 1.182
diff -p -c -r1.182 costsize.c
*** src/backend/optimizer/path/costsize.c	4 May 2007 01:13:44 -0000	1.182
--- src/backend/optimizer/path/costsize.c	17 May 2007 23:36:59 -0000
*************** cost_valuesscan(Path *path, PlannerInfo 
*** 905,910 ****
--- 905,950 ----
  }
  
  /*
+  * cost_samplescan
+  *	  Determines and returns the cost of scanning a base relation with
+  *    a TABLESAMPLE clause.
+  */
+ 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 sequential I/O. Conversely, for small
+ 	 * percentage samples, we're doing random I/O. For 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_sort
   *	  Determines and returns the cost of sorting a relation, including
   *	  the cost of reading the input data.
*************** approx_selectivity(PlannerInfo *root, Li
*** 2092,2105 ****
   *
   * 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.
   */
  void
  set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
  {
! 	double		nrows;
  
  	/* Should only be applied to base relations */
  	Assert(rel->relid > 0);
--- 2132,2146 ----
   *
   * 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.
   */
  void
  set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
  {
! 	double				 nrows;
! 	RangeTblEntry 		*rte;
  
  	/* Should only be applied to base relations */
  	Assert(rel->relid > 0);
*************** set_baserel_size_estimates(PlannerInfo *
*** 2110,2115 ****
--- 2151,2169 ----
  							   0,
  							   JOIN_INNER);
  
+ 	/*
+ 	 * Consider TABLESAMPLE, if any. We assume that 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
+ 	 * likely 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);
Index: src/backend/optimizer/plan/createplan.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/plan/createplan.c,v
retrieving revision 1.230
diff -p -c -r1.230 createplan.c
*** src/backend/optimizer/plan/createplan.c	4 May 2007 01:13:44 -0000	1.230
--- src/backend/optimizer/plan/createplan.c	17 May 2007 23:36:59 -0000
*************** static FunctionScan *create_functionscan
*** 62,67 ****
--- 62,69 ----
  						 List *tlist, List *scan_clauses);
  static ValuesScan *create_valuesscan_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 NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
  					 Plan *outer_plan, Plan *inner_plan);
  static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
*************** static FunctionScan *make_functionscan(L
*** 101,106 ****
--- 103,110 ----
  				  List *funccoltypes, List *funccoltypmods);
  static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
  				Index scanrelid, List *values_lists);
+ static SampleScan *make_samplescan(PlannerInfo *root, List *tlist,
+ 								   List *quals, Index scanrelid);
  static BitmapAnd *make_bitmap_and(List *bitmapplans);
  static BitmapOr *make_bitmap_or(List *bitmapplans);
  static NestLoop *make_nestloop(List *tlist,
*************** create_plan(PlannerInfo *root, Path *bes
*** 155,160 ****
--- 159,165 ----
  		case T_SubqueryScan:
  		case T_FunctionScan:
  		case T_ValuesScan:
+ 		case T_SampleScan:
  			plan = create_scan_plan(root, best_path);
  			break;
  		case T_HashJoin:
*************** create_scan_plan(PlannerInfo *root, Path
*** 278,283 ****
--- 283,295 ----
  												   scan_clauses);
  			break;
  
+ 		case T_SampleScan:
+ 			plan = (Plan *) create_samplescan_plan(root,
+ 												   best_path,
+ 												   tlist,
+ 												   scan_clauses);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized node type: %d",
  				 (int) best_path->pathtype);
*************** create_valuesscan_plan(PlannerInfo *root
*** 1406,1411 ****
--- 1418,1456 ----
  	return scan_plan;
  }
  
+ /*
+  * create_samplescan_path
+  *   Returns a SampleScan plan node for the base relation scanned by
+  *   'best_path', with the restriction clauses 'scan_clauses' and the
+  *   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 relation */
+ 	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);
+ 
+ 	scan_plan = make_samplescan(root, tlist,
+ 								scan_clauses,
+ 								scan_relid);
+ 
+ 	copy_path_costsize(&scan_plan->scan.plan, best_path);
+ 
+ 	return scan_plan;
+ }
+ 
  /*****************************************************************************
   *
   *	JOIN METHODS
*************** make_valuesscan(List *qptlist,
*** 2421,2426 ****
--- 2466,2507 ----
  	return node;
  }
  
+ /*
+  * make_samplescan
+  *   Allocate and return a SampleScan plan node, containing the
+  *   specified information ("tlist", "quals", and "scanrelid"). The
+  *   caller (create_samplescan_plan) fills in the cost of the plan,
+  *   and does the other hard work; we just allocate the node and fill
+  *   it in here.
+  */
+ static SampleScan *
+ make_samplescan(PlannerInfo *root,
+ 				List *tlist,
+ 				List *quals,
+ 				Index scanrelid)
+ {
+ 	SampleScan 			*node = makeNode(SampleScan);
+ 	Plan 				*plan = &node->scan.plan;
+ 	RangeTblEntry 		*rte;
+ 
+ 	/* cost should be inserted by caller */
+ 	plan->targetlist 	 = tlist;
+ 	plan->qual 			 = quals;
+ 	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;
+ }
+ 
  Append *
  make_append(List *appendplans, bool isTarget, List *tlist)
  {
Index: src/backend/optimizer/plan/setrefs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/plan/setrefs.c,v
retrieving revision 1.135
diff -p -c -r1.135 setrefs.c
*** src/backend/optimizer/plan/setrefs.c	30 Apr 2007 00:16:43 -0000	1.135
--- src/backend/optimizer/plan/setrefs.c	17 May 2007 23:36:59 -0000
*************** set_plan_refs(PlannerGlobal *glob, Plan 
*** 200,207 ****
  	switch (nodeTag(plan))
  	{
  		case T_SeqScan:
  			{
! 				SeqScan *splan = (SeqScan *) plan;
  
  				splan->scanrelid += rtoffset;
  				splan->plan.targetlist =
--- 200,208 ----
  	switch (nodeTag(plan))
  	{
  		case T_SeqScan:
+ 		case T_SampleScan:
  			{
! 				Scan *splan = (Scan *) plan;
  
  				splan->scanrelid += rtoffset;
  				splan->plan.targetlist =
Index: src/backend/optimizer/plan/subselect.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/plan/subselect.c,v
retrieving revision 1.122
diff -p -c -r1.122 subselect.c
*** src/backend/optimizer/plan/subselect.c	27 Feb 2007 01:11:25 -0000	1.122
--- src/backend/optimizer/plan/subselect.c	17 May 2007 23:36:59 -0000
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 1223,1228 ****
--- 1223,1229 ----
  
  		case T_Hash:
  		case T_Agg:
+ 		case T_SampleScan:
  		case T_SeqScan:
  		case T_Material:
  		case T_Sort:
Index: src/backend/optimizer/util/pathnode.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/util/pathnode.c,v
retrieving revision 1.140
diff -p -c -r1.140 pathnode.c
*** src/backend/optimizer/util/pathnode.c	4 May 2007 01:13:44 -0000	1.140
--- src/backend/optimizer/util/pathnode.c	17 May 2007 23:36:59 -0000
*************** create_valuesscan_path(PlannerInfo *root
*** 1147,1152 ****
--- 1147,1166 ----
  	return pathnode;
  }
  
+ 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_nestloop_path
   *	  Creates a pathnode corresponding to a nestloop join between two
Index: src/backend/optimizer/util/relnode.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/optimizer/util/relnode.c,v
retrieving revision 1.87
diff -p -c -r1.87 relnode.c
*** src/backend/optimizer/util/relnode.c	21 Apr 2007 21:01:45 -0000	1.87
--- src/backend/optimizer/util/relnode.c	17 May 2007 23:36:59 -0000
*************** build_simple_rel(PlannerInfo *root, int 
*** 83,88 ****
--- 83,89 ----
  	rel->tuples = 0;
  	rel->subplan = NULL;
  	rel->subrtable = NIL;
+ 	rel->has_table_sample = (rte->sample_info != NULL);
  	rel->baserestrictinfo = NIL;
  	rel->baserestrictcost.startup = 0;
  	rel->baserestrictcost.per_tuple = 0;
*************** build_join_rel(PlannerInfo *root,
*** 335,340 ****
--- 336,342 ----
  	joinrel->tuples = 0;
  	joinrel->subplan = NULL;
  	joinrel->subrtable = NIL;
+ 	joinrel->has_table_sample = false;
  	joinrel->baserestrictinfo = NIL;
  	joinrel->baserestrictcost.startup = 0;
  	joinrel->baserestrictcost.per_tuple = 0;
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.363
diff -p -c -r1.363 analyze.c
*** src/backend/parser/analyze.c	27 Apr 2007 22:05:48 -0000	1.363
--- src/backend/parser/analyze.c	17 May 2007 23:36:59 -0000
*************** transformInsertStmt(ParseState *pstate, 
*** 469,474 ****
--- 469,483 ----
  	ListCell   *attnos;
  	ListCell   *lc;
  
+ 	/*
+ 	 * 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;
  
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.591
diff -p -c -r2.591 gram.y
*** src/backend/parser/gram.y	27 Apr 2007 22:05:48 -0000	2.591
--- src/backend/parser/gram.y	18 May 2007 16:52:55 -0000
*************** static Node *makeXmlExpr(XmlExprOp op, c
*** 313,319 ****
--- 313,323 ----
  %type <jexpr>	joined_table
  %type <range>	relation_expr
  %type <range>	relation_expr_opt_alias
+ %type <range>   relation_expr_opt_sample
  %type <target>	target_el single_set_clause set_target insert_column_item
+ %type <node>    opt_table_sample
+ %type <boolean> sample_method
+ %type <value>	opt_repeatable_clause
  
  %type <typnam>	Typename SimpleTypename ConstTypename
  				GenericType Numeric opt_float
*************** static Node *makeXmlExpr(XmlExprOp op, c
*** 369,375 ****
  	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
  	ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
  
! 	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
  	BOOLEAN_P BOTH BY
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CHAIN CHAR_P
--- 373,379 ----
  	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
  	ASSERTION ASSIGNMENT ASYMMETRIC AT AUTHORIZATION
  
! 	BACKWARD BEFORE BEGIN_P BERNOULLI BETWEEN BIGINT BINARY BIT
  	BOOLEAN_P BOTH BY
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CHAIN CHAR_P
*************** static Node *makeXmlExpr(XmlExprOp op, c
*** 432,438 ****
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
  	SYMMETRIC SYSID SYSTEM_P
  
! 	TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
  	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P
  
--- 436,442 ----
  	STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
  	SYMMETRIC SYSID SYSTEM_P
  
! 	TABLE TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
  	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
  	TRUNCATE TRUSTED TYPE_P
  
*************** from_list:
*** 6259,6269 ****
   * and joined_table := '(' joined_table ')'.  So, we must have the
   * redundant-looking productions here instead.
   */
! table_ref:	relation_expr
  				{
  					$$ = (Node *) $1;
  				}
! 			| relation_expr alias_clause
  				{
  					$1->alias = $2;
  					$$ = (Node *) $1;
--- 6263,6273 ----
   * and joined_table := '(' joined_table ')'.  So, we must have the
   * redundant-looking productions here instead.
   */
! table_ref:	relation_expr_opt_sample
  				{
  					$$ = (Node *) $1;
  				}
! 			| relation_expr_opt_sample alias_clause
  				{
  					$1->alias = $2;
  					$$ = (Node *) $1;
*************** join_qual:	USING '(' name_list ')'					{
*** 6491,6496 ****
--- 6495,6520 ----
  		;
  
  
+ /*
+  * Pedagogical comment: the "relation_expr" production parses an
+  * identifier name, optionally schema qualified, and including an
+  * optional inheritance specification. It is used by the SELECT,
+  * DELETE, and UPDATE productions, as well as several DDL commands.
+  *
+  * 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;
+ 				}
+ 		;
+ 
  relation_expr:
  			qualified_name
  				{
*************** relation_expr:
*** 6522,6527 ****
--- 6546,6599 ----
  				}
  		;
  
+ opt_table_sample:
+ 			TABLESAMPLE sample_method '(' Iconst ')' opt_repeatable_clause
+ 			{
+ 				TableSampleInfo *n = makeNode(TableSampleInfo);
+ 
+ 				if ($2 == true)
+ 					n->sample_method = SAMPLE_BERNOULLI;
+ 				else
+ 					n->sample_method = SAMPLE_SYSTEM;
+ 
+ 				n->sample_percent = $4;
+ 				if ($4 > 100)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_SAMPLE_SIZE),
+ 							 errmsg("TABLESAMPLE percentage "
+ 									"cannot exceed 100")));
+ 				if ($4 <= 0)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_SAMPLE_SIZE),
+ 							 errmsg("TABLESAMPLE percentage must "
+ 									"be greater than 0")));
+ 
+ 				/* XXX: not supported yet */
+ 				if (n->sample_method == SAMPLE_BERNOULLI)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 							 errmsg("BERNOULLI sampling is not supported")));
+ 
+ 				if ($6 != NULL)
+ 				{
+ 					n->is_repeatable = true;
+ 					n->repeat_seed = intVal($6);
+ 				}
+ 
+ 				$$ = (Node *) n;
+ 			}
+ 			| /* EMPTY */					{ $$ = NULL; }
+ 		;
+ 
+ sample_method:
+ 			BERNOULLI						{ $$ = true; }
+ 			| SYSTEM_P						{ $$ = false; }
+ 		;
+ 
+ opt_repeatable_clause:
+ 			REPEATABLE '(' Iconst ')'		{ $$ = makeInteger($3); }
+ 			| /* EMPTY */					{ $$ = NULL; }
+ 		;
  
  /*
   * Given "UPDATE foo set set ...", we have to decide without looking any
*************** relation_expr:
*** 6532,6549 ****
   * 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;
--- 6604,6621 ----
   * 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;
*************** unreserved_keyword:
*** 8793,8798 ****
--- 8865,8871 ----
  			| BACKWARD
  			| BEFORE
  			| BEGIN_P
+ 			| BERNOULLI
  			| BY
  			| CACHE
  			| CALLED
*************** unreserved_keyword:
*** 8933,8939 ****
  			| RELATIVE_P
  			| RELEASE
  			| RENAME
- 			| REPEATABLE
  			| REPLACE
  			| REPLICA
  			| RESET
--- 9006,9011 ----
*************** type_func_name_keyword:
*** 9092,9098 ****
--- 9164,9172 ----
  			| OUTER_P
  			| OVERLAPS
  			| RIGHT
+ 			| REPEATABLE
  			| SIMILAR
+ 			| TABLESAMPLE
  			| VERBOSE
  		;
  
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.187
diff -p -c -r1.187 keywords.c
*** src/backend/parser/keywords.c	26 Apr 2007 16:13:12 -0000	1.187
--- src/backend/parser/keywords.c	17 May 2007 23:36:59 -0000
*************** static const ScanKeyword ScanKeywords[] 
*** 58,63 ****
--- 58,64 ----
  	{"backward", BACKWARD},
  	{"before", BEFORE},
  	{"begin", BEGIN_P},
+ 	{"bernoulli", BERNOULLI},
  	{"between", BETWEEN},
  	{"bigint", BIGINT},
  	{"binary", BINARY},
*************** static const ScanKeyword ScanKeywords[] 
*** 340,345 ****
--- 341,347 ----
  	{"sysid", SYSID},
  	{"system", SYSTEM_P},
  	{"table", TABLE},
+ 	{"tablesample", TABLESAMPLE},
  	{"tablespace", TABLESPACE},
  	{"temp", TEMP},
  	{"template", TEMPLATE},
Index: src/backend/parser/parse_clause.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/parser/parse_clause.c,v
retrieving revision 1.165
diff -p -c -r1.165 parse_clause.c
*** src/backend/parser/parse_clause.c	27 Apr 2007 22:05:48 -0000	1.165
--- src/backend/parser/parse_clause.c	17 May 2007 23:36:59 -0000
*************** setTargetTable(ParseState *pstate, Range
*** 164,169 ****
--- 164,175 ----
  										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));
Index: src/backend/parser/parse_relation.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/parser/parse_relation.c,v
retrieving revision 1.127
diff -p -c -r1.127 parse_relation.c
*** src/backend/parser/parse_relation.c	5 Jan 2007 22:19:34 -0000	1.127
--- src/backend/parser/parse_relation.c	17 May 2007 23:36:59 -0000
*************** searchRangeTable(ParseState *pstate, Ran
*** 209,215 ****
  
  			if (OidIsValid(relId) &&
  				rte->rtekind == RTE_RELATION &&
! 				rte->relid == relId)
  				return rte;
  			if (strcmp(rte->eref->aliasname, refname) == 0)
  				return rte;
--- 209,216 ----
  
  			if (OidIsValid(relId) &&
  				rte->rtekind == RTE_RELATION &&
! 				rte->relid == relId &&
! 				equal(rte->sample_info, relation->sample_info))
  				return rte;
  			if (strcmp(rte->eref->aliasname, refname) == 0)
  				return rte;
*************** addRangeTableEntry(ParseState *pstate,
*** 639,644 ****
--- 640,646 ----
  	lockmode = isLockedRel(pstate, refname) ? RowShareLock : AccessShareLock;
  	rel = heap_openrv(relation, lockmode);
  	rte->relid = RelationGetRelid(rel);
+ 	rte->sample_info = relation->sample_info;
  
  	/*
  	 * Build the list of effective column names using user-supplied aliases
*************** addRangeTableEntryForRelation(ParseState
*** 699,704 ****
--- 701,707 ----
  	rte->rtekind = RTE_RELATION;
  	rte->alias = alias;
  	rte->relid = RelationGetRelid(rel);
+ 	rte->sample_info = NULL;
  
  	/*
  	 * Build the list of effective column names using user-supplied aliases
*************** addRangeTableEntryForSubquery(ParseState
*** 754,759 ****
--- 757,763 ----
  
  	rte->rtekind = RTE_SUBQUERY;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = subquery;
  	rte->alias = alias;
  
*************** addRangeTableEntryForFunction(ParseState
*** 833,838 ****
--- 837,843 ----
  
  	rte->rtekind = RTE_FUNCTION;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = NULL;
  	rte->funcexpr = funcexpr;
  	rte->funccoltypes = NIL;
*************** addRangeTableEntryForValues(ParseState *
*** 959,964 ****
--- 964,970 ----
  
  	rte->rtekind = RTE_VALUES;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = NULL;
  	rte->values_lists = exprs;
  	rte->alias = alias;
*************** addRangeTableEntryForJoin(ParseState *ps
*** 1028,1033 ****
--- 1034,1040 ----
  
  	rte->rtekind = RTE_JOIN;
  	rte->relid = InvalidOid;
+ 	rte->sample_info = NULL;
  	rte->subquery = NULL;
  	rte->jointype = jointype;
  	rte->joinaliasvars = aliasvars;
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.257
diff -p -c -r1.257 ruleutils.c
*** src/backend/utils/adt/ruleutils.c	27 Mar 2007 23:21:10 -0000	1.257
--- src/backend/utils/adt/ruleutils.c	17 May 2007 23:36:59 -0000
*************** deparse_context_for(const char *aliasnam
*** 1429,1434 ****
--- 1429,1435 ----
  	rte = makeNode(RangeTblEntry);
  	rte->rtekind = RTE_RELATION;
  	rte->relid = relid;
+ 	rte->sample_info = NULL;
  	rte->eref = makeAlias(aliasname, NIL);
  	rte->inh = false;
  	rte->inFromCl = true;
*************** get_from_clause_item(Node *jtnode, Query
*** 4740,4745 ****
--- 4741,4775 ----
  			 */
  			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: %d",
+ 						 (int) sample_info->sample_method);
+ 					method_name = NULL;		/* keep compiler quiet */
+ 			}
+ 
+ 			appendStringInfo(buf, " TABLESAMPLE %s (%d)",
+ 							 method_name, sample_info->sample_percent);
+ 
+ 			if (sample_info->is_repeatable)
+ 				appendStringInfo(buf, " REPEATABLE (%d)",
+ 								 sample_info->repeat_seed);
+ 		}
  	}
  	else if (IsA(jtnode, JoinExpr))
  	{
Index: src/include/executor/nodeSamplescan.h
===================================================================
RCS file: src/include/executor/nodeSamplescan.h
diff -N src/include/executor/nodeSamplescan.h
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- src/include/executor/nodeSamplescan.h	17 May 2007 23:36:59 -0000
***************
*** 0 ****
--- 1,22 ----
+ /*-------------------------------------------------------------------------
+  *
+  * nodeSamplescan.h
+  *		Public interface to the TABLESAMPLE-based executor node
+  *
+  * Copyright (c) 2007, PostgreSQL Global Development Group
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef NODESAMPLESCAN_H
+ #define NODESAMPLESCAN_H
+ 
+ #include "nodes/execnodes.h"
+ 
+ extern SampleScanState *ExecInitSampleScan(SampleScan *node, EState *estate, int eflags);
+ extern TupleTableSlot *ExecSampleScan(SampleScanState *node);
+ extern int	ExecCountSlotsSampleScan(SampleScan *node);
+ extern void ExecEndSampleScan(SampleScanState *node);
+ 
+ #endif	/* NODESAMPLESCAN_H */
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/execnodes.h,v
retrieving revision 1.174
diff -p -c -r1.174 execnodes.h
*** src/include/nodes/execnodes.h	17 May 2007 19:35:08 -0000	1.174
--- src/include/nodes/execnodes.h	21 May 2007 18:11:42 -0000
*************** typedef struct ValuesScanState
*** 1140,1145 ****
--- 1140,1192 ----
  	int			marked_idx;
  } ValuesScanState;
  
+ /*
+  * 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.
+  *
+  * In addition to the fields of its parent class (ScanState), a
+  * SampleScanState contains:
+  *
+  *		cur_buf: the current buffer/page being scanned, if any. The
+  *		         sample scan holds a pin on this buffer while it is
+  *		         executing, to ensure it isn't evicted from the buffer
+  *		         pool while we're using it. InvalidBuffer if we
+  *		         haven't started the scan yet, or the scan has
+  *		         finished (reached the end of the heap).
+  *
+  *		cur_offset: the current offset in the buffer being scanned.
+  *
+  *		cur_blkno: the BlockNumber of cur_buf -- that is, cur_buf's
+  *		           position within the heap.
+  *
+  *		nblocks: the total # of blocks in the relation being scanned.
+  *		         Unless the sample percentage is 100, the scan likely
+  *		         won't visit this many blocks.
+  *
+  *		new_need_buf: have we run out of tuples on the current page?
+  *
+  *		cur_tup: current result tuple.
+  *
+  *		rand_state: PRNG state.
+  */
+ typedef struct SampleScanState
+ {
+ 	/* parent class; first field is NodeTag */
+ 	ScanState 			 ss;
+ 	Buffer 				 cur_buf;
+ 	OffsetNumber 		 cur_offset;
+ 	BlockNumber			 cur_blkno;
+ 	BlockNumber			 nblocks;
+ 	bool				 need_new_buf;
+ 	HeapTupleData		 cur_tup;
+ 	char 				 rand_state[128];
+ 	char 				*prev_rand_state;
+ } SampleScanState;
+ 
+ 
  /* ----------------------------------------------------------------
   *				 Join State Information
   * ----------------------------------------------------------------
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.199
diff -p -c -r1.199 nodes.h
*** src/include/nodes/nodes.h	26 Apr 2007 16:13:14 -0000	1.199
--- src/include/nodes/nodes.h	17 May 2007 23:36:59 -0000
*************** typedef enum NodeTag
*** 47,52 ****
--- 47,53 ----
  	T_BitmapAnd,
  	T_BitmapOr,
  	T_Scan,
+ 	T_SampleScan,
  	T_SeqScan,
  	T_IndexScan,
  	T_BitmapIndexScan,
*************** typedef enum NodeTag
*** 79,84 ****
--- 80,86 ----
  	T_BitmapAndState,
  	T_BitmapOrState,
  	T_ScanState,
+ 	T_SampleScanState,
  	T_SeqScanState,
  	T_IndexScanState,
  	T_BitmapIndexScanState,
*************** typedef enum NodeTag
*** 143,148 ****
--- 145,151 ----
  	T_JoinExpr,
  	T_FromExpr,
  	T_IntoClause,
+ 	T_TableSampleInfo,
  
  	/*
  	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.348
diff -p -c -r1.348 parsenodes.h
*** src/include/nodes/parsenodes.h	27 Apr 2007 22:05:49 -0000	1.348
--- src/include/nodes/parsenodes.h	17 May 2007 23:36:59 -0000
*************** typedef struct RangeTblEntry
*** 554,562 ****
  	 */
  
  	/*
! 	 * Fields valid for a plain relation RTE (else zero):
  	 */
! 	Oid			relid;			/* OID of the relation */
  
  	/*
  	 * Fields valid for a subquery RTE (else NULL):
--- 554,563 ----
  	 */
  
  	/*
! 	 * Fields valid for a plain relation RTE (else zero or NULL):
  	 */
! 	Oid					 relid;			/* OID of the relation */
! 	TableSampleInfo 	*sample_info;	/* TABLESAMPLE clause, if any */
  
  	/*
  	 * Fields valid for a subquery RTE (else NULL):
Index: src/include/nodes/plannodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/plannodes.h,v
retrieving revision 1.94
diff -p -c -r1.94 plannodes.h
*** src/include/nodes/plannodes.h	27 Apr 2007 22:05:49 -0000	1.94
--- src/include/nodes/plannodes.h	18 May 2007 05:29:08 -0000
*************** typedef struct ValuesScan
*** 359,364 ****
--- 359,383 ----
  	List	   *values_lists;	/* list of expression lists */
  } ValuesScan;
  
+ /* ----------------
+  *		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;
+ 
+ 
  /*
   * ==========
   * Join nodes
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/primnodes.h,v
retrieving revision 1.129
diff -p -c -r1.129 primnodes.h
*** src/include/nodes/primnodes.h	27 Mar 2007 23:21:12 -0000	1.129
--- src/include/nodes/primnodes.h	17 May 2007 23:36:59 -0000
*************** typedef enum OnCommitAction
*** 58,63 ****
--- 58,78 ----
  	ONCOMMIT_DROP				/* ON COMMIT DROP */
  } OnCommitAction;
  
+ typedef enum TableSampleMethod
+ {
+ 	SAMPLE_BERNOULLI,
+ 	SAMPLE_SYSTEM
+ } TableSampleMethod;
+ 
+ typedef struct TableSampleInfo
+ {
+ 	NodeTag				type;
+ 	TableSampleMethod 	sample_method;
+ 	int					sample_percent;
+ 	bool 				is_repeatable;
+ 	int 				repeat_seed;
+ } TableSampleInfo;
+ 
  /*
   * RangeVar - range variable, used in FROM clauses
   *
*************** typedef struct RangeVar
*** 76,81 ****
--- 91,97 ----
  								 * on children? */
  	bool		istemp;			/* is this a temp relation/sequence? */
  	Alias	   *alias;			/* table alias & optional column aliases */
+ 	TableSampleInfo *sample_info; /* TABLESAMPLE clause, if any */
  } RangeVar;
  
  /*
Index: src/include/nodes/relation.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.141
diff -p -c -r1.141 relation.h
*** src/include/nodes/relation.h	21 Apr 2007 21:01:45 -0000	1.141
--- src/include/nodes/relation.h	17 May 2007 23:36:59 -0000
*************** typedef struct RelOptInfo
*** 340,345 ****
--- 340,346 ----
  	double		tuples;
  	struct Plan *subplan;		/* if subquery */
  	List	   *subrtable;		/* if subquery */
+ 	bool		has_table_sample;
  
  	/* used by various scans and joins: */
  	List	   *baserestrictinfo;		/* RestrictInfo structures (if base
Index: src/include/optimizer/cost.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/optimizer/cost.h,v
retrieving revision 1.86
diff -p -c -r1.86 cost.h
*** src/include/optimizer/cost.h	4 May 2007 01:13:45 -0000	1.86
--- src/include/optimizer/cost.h	17 May 2007 23:36:59 -0000
*************** extern void cost_functionscan(Path *path
*** 72,77 ****
--- 72,79 ----
  				  RelOptInfo *baserel);
  extern void cost_valuesscan(Path *path, PlannerInfo *root,
  				RelOptInfo *baserel);
+ extern void cost_samplescan(Path *path, PlannerInfo *root,
+ 				RelOptInfo *baserel);
  extern void cost_sort(Path *path, PlannerInfo *root,
  		  List *pathkeys, Cost input_cost, double tuples, int width,
  		  double limit_tuples);
Index: src/include/optimizer/pathnode.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/optimizer/pathnode.h,v
retrieving revision 1.76
diff -p -c -r1.76 pathnode.h
*** src/include/optimizer/pathnode.h	20 Jan 2007 20:45:40 -0000	1.76
--- src/include/optimizer/pathnode.h	17 May 2007 23:36:59 -0000
*************** extern UniquePath *create_unique_path(Pl
*** 54,59 ****
--- 54,60 ----
  extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys);
  extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
+ extern Path *create_samplescan_path(PlannerInfo *root, RelOptInfo *rel);
  
  extern NestPath *create_nestloop_path(PlannerInfo *root,
  					 RelOptInfo *joinrel,
Index: src/include/utils/errcodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/utils/errcodes.h,v
retrieving revision 1.23
diff -p -c -r1.23 errcodes.h
*** src/include/utils/errcodes.h	3 Feb 2007 14:06:56 -0000	1.23
--- src/include/utils/errcodes.h	17 May 2007 23:36:59 -0000
***************
*** 131,136 ****
--- 131,137 ----
  #define ERRCODE_INVALID_LIMIT_VALUE			MAKE_SQLSTATE('2','2', '0','2','0')
  #define ERRCODE_INVALID_PARAMETER_VALUE		MAKE_SQLSTATE('2','2', '0','2','3')
  #define ERRCODE_INVALID_REGULAR_EXPRESSION	MAKE_SQLSTATE('2','2', '0','1','B')
+ #define ERRCODE_INVALID_SAMPLE_SIZE			MAKE_SQLSTATE('2','2', '0','2','H')
  #define ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE	MAKE_SQLSTATE('2','2', '0','0','9')
  #define ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER		MAKE_SQLSTATE('2','2', '0','0','C')
  #define ERRCODE_MOST_SPECIFIC_TYPE_MISMATCH MAKE_SQLSTATE('2','2', '0','0','G')
