Commit dd7a8f66 authored by Tom Lane's avatar Tom Lane

Redesign tablesample method API, and do extensive code review.

The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM.  (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.)  Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type.  This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.

Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.

Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.

Back-patch to 9.5 so that we don't have to support the original API
in production.
parent b26e3d66
......@@ -2297,6 +2297,7 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
{
case RTE_RELATION:
APP_JUMB(rte->relid);
JumbleExpr(jstate, (Node *) rte->tablesample);
break;
case RTE_SUBQUERY:
JumbleQuery(jstate, rte->subquery);
......@@ -2767,6 +2768,15 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, rtfunc->funcexpr);
}
break;
case T_TableSampleClause:
{
TableSampleClause *tsc = (TableSampleClause *) node;
APP_JUMB(tsc->tsmhandler);
JumbleExpr(jstate, (Node *) tsc->args);
JumbleExpr(jstate, (Node *) tsc->repeatable);
}
break;
default:
/* Only a warning, since we can stumble along anyway */
elog(WARNING, "unrecognized node type: %d",
......
# src/test/modules/tsm_system_rows/Makefile
# contrib/tsm_system_rows/Makefile
MODULE_big = tsm_system_rows
OBJS = tsm_system_rows.o $(WIN32RES)
PGFILEDESC = "tsm_system_rows - SYSTEM TABLESAMPLE method which accepts number of rows as a limit"
PGFILEDESC = "tsm_system_rows - TABLESAMPLE method which accepts number of rows as a limit"
EXTENSION = tsm_system_rows
DATA = tsm_system_rows--1.0.sql
......
CREATE EXTENSION tsm_system_rows;
CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- force smaller pages so we don't have to load too much data to get multiple pages
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000) FROM generate_series(0, 30) s(i) ORDER BY i;
CREATE TABLE test_tablesample (id int, name text);
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000)
FROM generate_series(0, 30) s(i);
ANALYZE test_tablesample;
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (1000);
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (0);
count
-------
0
(1 row)
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (1);
count
-------
1
(1 row)
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (10);
count
-------
10
(1 row)
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (100);
count
-------
31
(1 row)
SELECT id FROM test_tablesample TABLESAMPLE system_rows (8) REPEATABLE (5432);
id
----
7
14
21
28
4
11
18
25
(8 rows)
EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE system_rows (20) REPEATABLE (10);
QUERY PLAN
-----------------------------------------------------------------------------------
Sample Scan (system_rows) on test_tablesample (cost=0.00..80.20 rows=20 width=4)
-- bad parameters should get through planning, but not execution:
EXPLAIN (COSTS OFF)
SELECT id FROM test_tablesample TABLESAMPLE system_rows (-1);
QUERY PLAN
----------------------------------------
Sample Scan on test_tablesample
Sampling: system_rows ('-1'::bigint)
(2 rows)
SELECT id FROM test_tablesample TABLESAMPLE system_rows (-1);
ERROR: sample size must not be negative
-- fail, this method is not repeatable:
SELECT * FROM test_tablesample TABLESAMPLE system_rows (10) REPEATABLE (0);
ERROR: tablesample method system_rows does not support REPEATABLE
LINE 1: SELECT * FROM test_tablesample TABLESAMPLE system_rows (10) ...
^
-- but a join should be allowed:
EXPLAIN (COSTS OFF)
SELECT * FROM
(VALUES (0),(10),(100)) v(nrows),
LATERAL (SELECT count(*) FROM test_tablesample
TABLESAMPLE system_rows (nrows)) ss;
QUERY PLAN
----------------------------------------------------------
Nested Loop
-> Values Scan on "*VALUES*"
-> Aggregate
-> Sample Scan on test_tablesample
Sampling: system_rows ("*VALUES*".column1)
(5 rows)
SELECT * FROM
(VALUES (0),(10),(100)) v(nrows),
LATERAL (SELECT count(*) FROM test_tablesample
TABLESAMPLE system_rows (nrows)) ss;
nrows | count
-------+-------
0 | 0
10 | 10
100 | 31
(3 rows)
CREATE VIEW vv AS
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (20);
SELECT * FROM vv;
count
-------
20
(1 row)
-- done
DROP TABLE test_tablesample CASCADE;
DROP EXTENSION tsm_system_rows; -- fail, view depends on extension
ERROR: cannot drop extension tsm_system_rows because other objects depend on it
DETAIL: view vv depends on function system_rows(internal)
HINT: Use DROP ... CASCADE to drop the dependent objects too.
CREATE EXTENSION tsm_system_rows;
CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- force smaller pages so we don't have to load too much data to get multiple pages
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000) FROM generate_series(0, 30) s(i) ORDER BY i;
CREATE TABLE test_tablesample (id int, name text);
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000)
FROM generate_series(0, 30) s(i);
ANALYZE test_tablesample;
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (1000);
SELECT id FROM test_tablesample TABLESAMPLE system_rows (8) REPEATABLE (5432);
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (0);
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (1);
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (10);
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (100);
-- bad parameters should get through planning, but not execution:
EXPLAIN (COSTS OFF)
SELECT id FROM test_tablesample TABLESAMPLE system_rows (-1);
SELECT id FROM test_tablesample TABLESAMPLE system_rows (-1);
-- fail, this method is not repeatable:
SELECT * FROM test_tablesample TABLESAMPLE system_rows (10) REPEATABLE (0);
-- but a join should be allowed:
EXPLAIN (COSTS OFF)
SELECT * FROM
(VALUES (0),(10),(100)) v(nrows),
LATERAL (SELECT count(*) FROM test_tablesample
TABLESAMPLE system_rows (nrows)) ss;
SELECT * FROM
(VALUES (0),(10),(100)) v(nrows),
LATERAL (SELECT count(*) FROM test_tablesample
TABLESAMPLE system_rows (nrows)) ss;
CREATE VIEW vv AS
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (20);
EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE system_rows (20) REPEATABLE (10);
SELECT * FROM vv;
-- done
DROP TABLE test_tablesample CASCADE;
DROP EXTENSION tsm_system_rows; -- fail, view depends on extension
/* src/test/modules/tablesample/tsm_system_rows--1.0.sql */
/* contrib/tsm_system_rows/tsm_system_rows--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION tsm_system_rows" to load this file. \quit
CREATE FUNCTION tsm_system_rows_init(internal, int4, int4)
RETURNS void
AS 'MODULE_PATHNAME'
CREATE FUNCTION system_rows(internal)
RETURNS tsm_handler
AS 'MODULE_PATHNAME', 'tsm_system_rows_handler'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_rows_nextblock(internal)
RETURNS int4
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_rows_nexttuple(internal, int4, int2)
RETURNS int2
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_rows_examinetuple(internal, int4, internal, bool)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_rows_end(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_rows_reset(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_rows_cost(internal, internal, internal, internal, internal, internal, internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
INSERT INTO pg_tablesample_method VALUES('system_rows', false, true,
'tsm_system_rows_init', 'tsm_system_rows_nextblock',
'tsm_system_rows_nexttuple', 'tsm_system_rows_examinetuple',
'tsm_system_rows_end', 'tsm_system_rows_reset', 'tsm_system_rows_cost');
This diff is collapsed.
# tsm_system_rows extension
comment = 'SYSTEM TABLESAMPLE method which accepts number rows as a limit'
comment = 'TABLESAMPLE method which accepts number of rows as a limit'
default_version = '1.0'
module_pathname = '$libdir/tsm_system_rows'
relocatable = true
# src/test/modules/tsm_system_time/Makefile
# contrib/tsm_system_time/Makefile
MODULE_big = tsm_system_time
OBJS = tsm_system_time.o $(WIN32RES)
PGFILEDESC = "tsm_system_time - SYSTEM TABLESAMPLE method which accepts number rows of as a limit"
PGFILEDESC = "tsm_system_time - TABLESAMPLE method which accepts time in milliseconds as a limit"
EXTENSION = tsm_system_time
DATA = tsm_system_time--1.0.sql
......
CREATE EXTENSION tsm_system_time;
CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- force smaller pages so we don't have to load too much data to get multiple pages
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000) FROM generate_series(0, 30) s(i) ORDER BY i;
CREATE TABLE test_tablesample (id int, name text);
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000)
FROM generate_series(0, 30) s(i);
ANALYZE test_tablesample;
SELECT count(*) FROM test_tablesample TABLESAMPLE system_time (1000);
-- It's a bit tricky to test SYSTEM_TIME in a platform-independent way.
-- We can test the zero-time corner case ...
SELECT count(*) FROM test_tablesample TABLESAMPLE system_time (0);
count
-------
31
0
(1 row)
SELECT id FROM test_tablesample TABLESAMPLE system_time (1000) REPEATABLE (5432);
id
----
7
14
21
28
4
11
18
25
1
8
15
22
29
5
12
19
26
2
9
16
23
30
6
13
20
27
3
10
17
24
0
(31 rows)
EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE system_time (100) REPEATABLE (10);
QUERY PLAN
------------------------------------------------------------------------------------
Sample Scan (system_time) on test_tablesample (cost=0.00..100.25 rows=25 width=4)
-- ... and we assume that this will finish before running out of time:
SELECT count(*) FROM test_tablesample TABLESAMPLE system_time (100000);
count
-------
31
(1 row)
-- done
DROP TABLE test_tablesample CASCADE;
-- bad parameters should get through planning, but not execution:
EXPLAIN (COSTS OFF)
SELECT id FROM test_tablesample TABLESAMPLE system_time (-1);
QUERY PLAN
--------------------------------------------------
Sample Scan on test_tablesample
Sampling: system_time ('-1'::double precision)
(2 rows)
SELECT id FROM test_tablesample TABLESAMPLE system_time (-1);
ERROR: sample collection time must not be negative
-- fail, this method is not repeatable:
SELECT * FROM test_tablesample TABLESAMPLE system_time (10) REPEATABLE (0);
ERROR: tablesample method system_time does not support REPEATABLE
LINE 1: SELECT * FROM test_tablesample TABLESAMPLE system_time (10) ...
^
-- since it's not repeatable, we expect a Materialize node in these plans:
EXPLAIN (COSTS OFF)
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (100000)) ss;
QUERY PLAN
------------------------------------------------------------------------
Nested Loop
-> Aggregate
-> Materialize
-> Sample Scan on test_tablesample
Sampling: system_time ('100000'::double precision)
-> Values Scan on "*VALUES*"
(6 rows)
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (100000)) ss;
time | count
--------+-------
0 | 31
100000 | 31
(2 rows)
EXPLAIN (COSTS OFF)
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (time)) ss;
QUERY PLAN
----------------------------------------------------------------
Nested Loop
-> Values Scan on "*VALUES*"
-> Aggregate
-> Materialize
-> Sample Scan on test_tablesample
Sampling: system_time ("*VALUES*".column1)
(6 rows)
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (time)) ss;
time | count
--------+-------
0 | 0
100000 | 31
(2 rows)
CREATE VIEW vv AS
SELECT * FROM test_tablesample TABLESAMPLE system_time (20);
EXPLAIN (COSTS OFF) SELECT * FROM vv;
QUERY PLAN
--------------------------------------------------
Sample Scan on test_tablesample
Sampling: system_time ('20'::double precision)
(2 rows)
DROP EXTENSION tsm_system_time; -- fail, view depends on extension
ERROR: cannot drop extension tsm_system_time because other objects depend on it
DETAIL: view vv depends on function system_time(internal)
HINT: Use DROP ... CASCADE to drop the dependent objects too.
CREATE EXTENSION tsm_system_time;
CREATE TABLE test_tablesample (id int, name text) WITH (fillfactor=10); -- force smaller pages so we don't have to load too much data to get multiple pages
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000) FROM generate_series(0, 30) s(i) ORDER BY i;
CREATE TABLE test_tablesample (id int, name text);
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000)
FROM generate_series(0, 30) s(i);
ANALYZE test_tablesample;
SELECT count(*) FROM test_tablesample TABLESAMPLE system_time (1000);
SELECT id FROM test_tablesample TABLESAMPLE system_time (1000) REPEATABLE (5432);
-- It's a bit tricky to test SYSTEM_TIME in a platform-independent way.
-- We can test the zero-time corner case ...
SELECT count(*) FROM test_tablesample TABLESAMPLE system_time (0);
-- ... and we assume that this will finish before running out of time:
SELECT count(*) FROM test_tablesample TABLESAMPLE system_time (100000);
-- bad parameters should get through planning, but not execution:
EXPLAIN (COSTS OFF)
SELECT id FROM test_tablesample TABLESAMPLE system_time (-1);
SELECT id FROM test_tablesample TABLESAMPLE system_time (-1);
-- fail, this method is not repeatable:
SELECT * FROM test_tablesample TABLESAMPLE system_time (10) REPEATABLE (0);
-- since it's not repeatable, we expect a Materialize node in these plans:
EXPLAIN (COSTS OFF)
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (100000)) ss;
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (100000)) ss;
EXPLAIN (COSTS OFF)
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (time)) ss;
SELECT * FROM
(VALUES (0),(100000)) v(time),
LATERAL (SELECT COUNT(*) FROM test_tablesample
TABLESAMPLE system_time (time)) ss;
CREATE VIEW vv AS
SELECT * FROM test_tablesample TABLESAMPLE system_time (20);
EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE system_time (100) REPEATABLE (10);
EXPLAIN (COSTS OFF) SELECT * FROM vv;
-- done
DROP TABLE test_tablesample CASCADE;
DROP EXTENSION tsm_system_time; -- fail, view depends on extension
/* src/test/modules/tablesample/tsm_system_time--1.0.sql */
/* contrib/tsm_system_time/tsm_system_time--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION tsm_system_time" to load this file. \quit
CREATE FUNCTION tsm_system_time_init(internal, int4, int4)
RETURNS void
AS 'MODULE_PATHNAME'
CREATE FUNCTION system_time(internal)
RETURNS tsm_handler
AS 'MODULE_PATHNAME', 'tsm_system_time_handler'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_time_nextblock(internal)
RETURNS int4
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_time_nexttuple(internal, int4, int2)
RETURNS int2
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_time_end(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_time_reset(internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION tsm_system_time_cost(internal, internal, internal, internal, internal, internal, internal)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
INSERT INTO pg_tablesample_method VALUES('system_time', false, true,
'tsm_system_time_init', 'tsm_system_time_nextblock',
'tsm_system_time_nexttuple', '-', 'tsm_system_time_end',
'tsm_system_time_reset', 'tsm_system_time_cost');
This diff is collapsed.
# tsm_system_time extension
comment = 'SYSTEM TABLESAMPLE method which accepts time in milliseconds as a limit'
comment = 'TABLESAMPLE method which accepts time in milliseconds as a limit'
default_version = '1.0'
module_pathname = '$libdir/tsm_system_time'
relocatable = true
......@@ -278,11 +278,6 @@
<entry>planner statistics</entry>
</row>
<row>
<entry><link linkend="catalog-pg-tablesample-method"><structname>pg_tablesample_method</structname></link></entry>
<entry>table sampling methods</entry>
</row>
<row>
<entry><link linkend="catalog-pg-tablespace"><structname>pg_tablespace</structname></link></entry>
<entry>tablespaces within this database cluster</entry>
......@@ -6132,121 +6127,6 @@
</sect1>
<sect1 id="catalog-pg-tablesample-method">
<title><structname>pg_tabesample_method</structname></title>
<indexterm zone="catalog-pg-tablesample-method">
<primary>pg_am</primary>
</indexterm>
<para>
The catalog <structname>pg_tablesample_method</structname> stores
information about table sampling methods which can be used in
<command>TABLESAMPLE</command> clause of a <command>SELECT</command>
statement.
</para>
<table>
<title><structname>pg_tablesample_method</> Columns</title>
<tgroup cols="4">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>oid</structfield></entry>
<entry><type>oid</type></entry>
<entry></entry>
<entry>Row identifier (hidden attribute; must be explicitly selected)</entry>
</row>
<row>
<entry><structfield>tsmname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Name of the sampling method</entry>
</row>
<row>
<entry><structfield>tsmseqscan</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>If true, the sampling method scans the whole table sequentially.
</entry>
</row>
<row>
<entry><structfield>tsmpagemode</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>If true, the sampling method always reads the pages completely.
</entry>
</row>
<row>
<entry><structfield>tsminit</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry><quote>Initialize the sampling scan</quote> function</entry>
</row>
<row>
<entry><structfield>tsmnextblock</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry><quote>Get next block number</quote> function</entry>
</row>
<row>
<entry><structfield>tsmnexttuple</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry><quote>Get next tuple offset</quote> function</entry>
</row>
<row>
<entry><structfield>tsmexaminetuple</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry>Function which examines the tuple contents and decides if to
return it, or zero if none</entry>
</row>
<row>
<entry><structfield>tsmend</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry><quote>End the sampling scan</quote> function</entry>
</row>
<row>
<entry><structfield>tsmreset</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry><quote>Restart the state of sampling scan</quote> function</entry>
</row>
<row>
<entry><structfield>tsmcost</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
<entry>Costing function</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="catalog-pg-tablespace">
<title><structname>pg_tablespace</structname></title>
......
......@@ -4346,7 +4346,7 @@ SET xmloption TO { DOCUMENT | CONTENT };
an object identifier. There are also several alias types for
<type>oid</>: <type>regproc</>, <type>regprocedure</>,
<type>regoper</>, <type>regoperator</>, <type>regclass</>,
<type>regtype</>, <type>regrole</>, <type>regnamespace</>,
<type>regtype</>, <type>regrole</>, <type>regnamespace</>,
<type>regconfig</>, and <type>regdictionary</>.
<xref linkend="datatype-oid-table"> shows an overview.
</para>
......@@ -4622,6 +4622,10 @@ SELECT * FROM pg_attribute
<primary>fdw_handler</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>tsm_handler</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>cstring</primary>
</indexterm>
......@@ -4716,6 +4720,11 @@ SELECT * FROM pg_attribute
<entry>A foreign-data wrapper handler is declared to return <type>fdw_handler</>.</entry>
</row>
<row>
<entry><type>tsm_handler</></entry>
<entry>A tablesample method handler is declared to return <type>tsm_handler</>.</entry>
</row>
<row>
<entry><type>record</></entry>
<entry>Identifies a function returning an unspecified row type.</entry>
......
......@@ -243,6 +243,7 @@
&nls;
&plhandler;
&fdwhandler;
&tablesample-method;
&custom-scan;
&geqo;
&indexam;
......@@ -250,7 +251,6 @@
&spgist;
&gin;
&brin;
&tablesample-method;
&storage;
&bki;
&planstats;
......
......@@ -49,7 +49,8 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac
<phrase>where <replaceable class="parameter">from_item</replaceable> can be one of:</phrase>
[ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ] [ TABLESAMPLE <replaceable class="parameter">sampling_method</replaceable> ( <replaceable class="parameter">argument</replaceable> [, ...] ) [ REPEATABLE ( <replaceable class="parameter">seed</replaceable> ) ] ]
[ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
[ TABLESAMPLE <replaceable class="parameter">sampling_method</replaceable> ( <replaceable class="parameter">argument</replaceable> [, ...] ) [ REPEATABLE ( <replaceable class="parameter">seed</replaceable> ) ] ]
[ LATERAL ] ( <replaceable class="parameter">select</replaceable> ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ]
<replaceable class="parameter">with_query_name</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] )
......@@ -325,50 +326,6 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
</listitem>
</varlistentry>
<varlistentry>
<term>TABLESAMPLE <replaceable class="parameter">sampling_method</replaceable> ( <replaceable class="parameter">argument</replaceable> [, ...] ) [ REPEATABLE ( <replaceable class="parameter">seed</replaceable> ) ]</term>
<listitem>
<para>
Table sample clause after
<replaceable class="parameter">table_name</replaceable> indicates that
a <replaceable class="parameter">sampling_method</replaceable> should
be used to retrieve subset of rows in the table.
The <replaceable class="parameter">sampling_method</replaceable> can be
any sampling method installed in the database. There are currently two
sampling methods available in the standard
<productname>PostgreSQL</productname> distribution:
<itemizedlist>
<listitem>
<para><literal>SYSTEM</literal></para>
</listitem>
<listitem>
<para><literal>BERNOULLI</literal></para>
</listitem>
</itemizedlist>
Both of these sampling methods currently accept only single argument
which is the percent (floating point from 0 to 100) of the rows to
be returned.
The <literal>SYSTEM</literal> sampling method does block level
sampling with each block having the same chance of being selected and
returns all rows from each selected block.
The <literal>BERNOULLI</literal> scans whole table and returns
individual rows with equal probability. Additional sampling methods
may be installed in the database via extensions.
</para>
<para>
The optional parameter <literal>REPEATABLE</literal> uses the seed
parameter, which can be a number or expression producing a number, as
a random seed for sampling. Note that subsequent commands may return
different results even if same <literal>REPEATABLE</literal> clause was
specified. This happens because <acronym>DML</acronym> statements and
maintenance operations such as <command>VACUUM</> may affect physical
distribution of data. The <function>setseed()</> function will not
affect the sampling result when the <literal>REPEATABLE</literal>
parameter is used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">alias</replaceable></term>
<listitem>
......@@ -387,6 +344,61 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
</listitem>
</varlistentry>
<varlistentry>
<term><literal>TABLESAMPLE <replaceable class="parameter">sampling_method</replaceable> ( <replaceable class="parameter">argument</replaceable> [, ...] ) [ REPEATABLE ( <replaceable class="parameter">seed</replaceable> ) ]</literal></term>
<listitem>
<para>
A <literal>TABLESAMPLE</> clause after
a <replaceable class="parameter">table_name</> indicates that the
specified <replaceable class="parameter">sampling_method</replaceable>
should be used to retrieve a subset of the rows in that table.
This sampling precedes the application of any other filters such
as <literal>WHERE</> clauses.
The standard <productname>PostgreSQL</productname> distribution
includes two sampling methods, <literal>BERNOULLI</literal>
and <literal>SYSTEM</literal>, and other sampling methods can be
installed in the database via extensions.
</para>
<para>
The <literal>BERNOULLI</> and <literal>SYSTEM</> sampling methods
each accept a single <replaceable class="parameter">argument</>
which is the fraction of the table to sample, expressed as a
percentage between 0 and 100. This argument can be
any <type>real</>-valued expression. (Other sampling methods might
accept more or different arguments.) These two methods each return
a randomly-chosen sample of the table that will contain
approximately the specified percentage of the table's rows.
The <literal>BERNOULLI</literal> method scans the whole table and
selects or ignores individual rows independently with the specified
probability.
The <literal>SYSTEM</literal> method does block-level sampling with
each block having the specified chance of being selected; all rows
in each selected block are returned.
The <literal>SYSTEM</literal> method is significantly faster than
the <literal>BERNOULLI</literal> method when small sampling
percentages are specified, but it may return a less-random sample of
the table as a result of clustering effects.
</para>
<para>
The optional <literal>REPEATABLE</literal> clause specifies
a <replaceable class="parameter">seed</> number or expression to use
for generating random numbers within the sampling method. The seed
value can be any non-null floating-point value. Two queries that
specify the same seed and <replaceable class="parameter">argument</>
values will select the same sample of the table, if the table has
not been changed meanwhile. But different seed values will usually
produce different samples.
If <literal>REPEATABLE</literal> is not given then a new random
sample is selected for each query.
Note that some add-on sampling methods do not
accept <literal>REPEATABLE</literal>, and will always produce new
samples on each use.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">select</replaceable></term>
<listitem>
......@@ -1870,6 +1882,16 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
</para>
</refsect2>
<refsect2>
<title><literal>TABLESAMPLE</literal> Clause Restrictions</title>
<para>
The <literal>TABLESAMPLE</> clause is currently accepted only on
regular tables and materialized views. According to the SQL standard
it should be possible to apply it to any <literal>FROM</> item.
</para>
</refsect2>
<refsect2>
<title>Function Calls in <literal>FROM</literal></title>
......@@ -1993,19 +2015,5 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
</para>
</refsect2>
<refsect2>
<title><literal>TABLESAMPLE</literal> clause</title>
<para>
The <literal>TABLESAMPLE</> clause is currently accepted only on physical
relations and materialized views.
</para>
<para>
Additional modules allow you to install custom sampling methods and use
them instead of the SQL standard methods.
</para>
</refsect2>
</refsect1>
</refentry>
This diff is collapsed.
......@@ -8,24 +8,37 @@
</indexterm>
<para>
The <filename>tsm_system_rows</> module provides the tablesample method
<literal>SYSTEM_ROWS</literal>, which can be used inside the
<command>TABLESAMPLE</command> clause of a <command>SELECT</command>.
The <filename>tsm_system_rows</> module provides the table sampling method
<literal>SYSTEM_ROWS</literal>, which can be used in
the <literal>TABLESAMPLE</> clause of a <xref linkend="sql-select">
command.
</para>
<para>
This tablesample method uses a linear probing algorithm to read sample
of a table and uses actual number of rows as limit (unlike the
<literal>SYSTEM</literal> tablesample method which limits by percentage
of a table).
This table sampling method accepts a single integer argument that is the
maximum number of rows to read. The resulting sample will always contain
exactly that many rows, unless the table does not contain enough rows, in
which case the whole table is selected.
</para>
<para>
Like the built-in <literal>SYSTEM</literal> sampling
method, <literal>SYSTEM_ROWS</literal> performs block-level sampling, so
that the sample is not completely random but may be subject to clustering
effects, especially if only a small number of rows are requested.
</para>
<para>
<literal>SYSTEM_ROWS</literal> does not support
the <literal>REPEATABLE</literal> clause.
</para>
<sect2>
<title>Examples</title>
<para>
Here is an example of selecting sample of a table with
<literal>SYSTEM_ROWS</>. First install the extension:
Here is an example of selecting a sample of a table with
<literal>SYSTEM_ROWS</>. First install the extension:
</para>
<programlisting>
......@@ -33,8 +46,7 @@ CREATE EXTENSION tsm_system_rows;
</programlisting>
<para>
Then you can use it in <command>SELECT</command> command same way as other
tablesample methods:
Then you can use it in a <command>SELECT</command> command, for instance:
<programlisting>
SELECT * FROM my_table TABLESAMPLE SYSTEM_ROWS(100);
......@@ -42,8 +54,9 @@ SELECT * FROM my_table TABLESAMPLE SYSTEM_ROWS(100);
</para>
<para>
The above command will return a sample of 100 rows from the table my_table
(less if the table does not have 100 visible rows).
This command will return a sample of 100 rows from the
table <structname>my_table</> (unless the table does not have 100
visible rows, in which case all its rows are returned).
</para>
</sect2>
......
......@@ -8,25 +8,39 @@
</indexterm>
<para>
The <filename>tsm_system_time</> module provides the tablesample method
<literal>SYSTEM_TIME</literal>, which can be used inside the
<command>TABLESAMPLE</command> clause of a <command>SELECT</command>.
The <filename>tsm_system_time</> module provides the table sampling method
<literal>SYSTEM_TIME</literal>, which can be used in
the <literal>TABLESAMPLE</> clause of a <xref linkend="sql-select">
command.
</para>
<para>
This tablesample method uses a linear probing algorithm to read sample
of a table and uses time in milliseconds as limit (unlike the
<literal>SYSTEM</literal> tablesample method which limits by percentage
of a table). This gives you some control over the length of execution
of your query.
This table sampling method accepts a single floating-point argument that
is the maximum number of milliseconds to spend reading the table. This
gives you direct control over how long the query takes, at the price that
the size of the sample becomes hard to predict. The resulting sample will
contain as many rows as could be read in the specified time, unless the
whole table has been read first.
</para>
<para>
Like the built-in <literal>SYSTEM</literal> sampling
method, <literal>SYSTEM_TIME</literal> performs block-level sampling, so
that the sample is not completely random but may be subject to clustering
effects, especially if only a small number of rows are selected.
</para>
<para>
<literal>SYSTEM_TIME</literal> does not support
the <literal>REPEATABLE</literal> clause.
</para>
<sect2>
<title>Examples</title>
<para>
Here is an example of selecting sample of a table with
<literal>SYSTEM_TIME</>. First install the extension:
Here is an example of selecting a sample of a table with
<literal>SYSTEM_TIME</>. First install the extension:
</para>
<programlisting>
......@@ -34,8 +48,7 @@ CREATE EXTENSION tsm_system_time;
</programlisting>
<para>
Then you can use it in a <command>SELECT</command> command the same way as
other tablesample methods:
Then you can use it in a <command>SELECT</command> command, for instance:
<programlisting>
SELECT * FROM my_table TABLESAMPLE SYSTEM_TIME(1000);
......@@ -43,8 +56,9 @@ SELECT * FROM my_table TABLESAMPLE SYSTEM_TIME(1000);
</para>
<para>
The above command will return as large a sample of my_table as it can read in
1 second (or less if it reads whole table faster).
This command will return as large a sample of <structname>my_table</> as
it can read in 1 second (1000 milliseconds). Of course, if the whole
table can be read in under 1 second, all its rows will be returned.
</para>
</sect2>
......
......@@ -80,8 +80,11 @@ bool synchronize_seqscans = true;
static HeapScanDesc heap_beginscan_internal(Relation relation,
Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode,
bool is_bitmapscan, bool is_samplescan,
bool allow_strat,
bool allow_sync,
bool allow_pagemode,
bool is_bitmapscan,
bool is_samplescan,
bool temp_snap);
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
......@@ -207,7 +210,7 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
* ----------------
*/
static void
initscan(HeapScanDesc scan, ScanKey key, bool is_rescan)
initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock)
{
bool allow_strat;
bool allow_sync;
......@@ -257,12 +260,12 @@ initscan(HeapScanDesc scan, ScanKey key, bool is_rescan)
scan->rs_strategy = NULL;
}
if (is_rescan)
if (keep_startblock)
{
/*
* If rescan, keep the previous startblock setting so that rewinding a
* cursor doesn't generate surprising results. Reset the syncscan
* setting, though.
* When rescanning, we want to keep the previous startblock setting,
* so that rewinding a cursor doesn't generate surprising results.
* Reset the active syncscan setting, though.
*/
scan->rs_syncscan = (allow_sync && synchronize_seqscans);
}
......@@ -1313,6 +1316,10 @@ heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
/* ----------------
* heap_beginscan - begin relation scan
*
* heap_beginscan is the "standard" case.
*
* heap_beginscan_catalog differs in setting up its own temporary snapshot.
*
* heap_beginscan_strat offers an extended API that lets the caller control
* whether a nondefault buffer access strategy can be used, and whether
* syncscan can be chosen (possibly resulting in the scan not starting from
......@@ -1323,8 +1330,11 @@ heap_openrv_extended(const RangeVar *relation, LOCKMODE lockmode,
* really quite unlike a standard seqscan, there is just enough commonality
* to make it worth using the same data structure.
*
* heap_beginscan_samplingscan is alternate entry point for setting up a
* HeapScanDesc for a TABLESAMPLE scan.
* heap_beginscan_sampling is an alternative entry point for setting up a
* HeapScanDesc for a TABLESAMPLE scan. As with bitmap scans, it's worth
* using the same data structure although the behavior is rather different.
* In addition to the options offered by heap_beginscan_strat, this call
* also allows control of whether page-mode visibility checking is used.
* ----------------
*/
HeapScanDesc
......@@ -1366,18 +1376,22 @@ heap_beginscan_bm(Relation relation, Snapshot snapshot,
HeapScanDesc
heap_beginscan_sampling(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_pagemode)
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
return heap_beginscan_internal(relation, snapshot, nkeys, key,
allow_strat, false, allow_pagemode,
allow_strat, allow_sync, allow_pagemode,
false, true, false);
}
static HeapScanDesc
heap_beginscan_internal(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode,
bool is_bitmapscan, bool is_samplescan, bool temp_snap)
bool allow_strat,
bool allow_sync,
bool allow_pagemode,
bool is_bitmapscan,
bool is_samplescan,
bool temp_snap)
{
HeapScanDesc scan;
......@@ -1461,6 +1475,27 @@ heap_rescan(HeapScanDesc scan,
initscan(scan, key, true);
}
/* ----------------
* heap_rescan_set_params - restart a relation scan after changing params
*
* This call allows changing the buffer strategy, syncscan, and pagemode
* options before starting a fresh scan. Note that although the actual use
* of syncscan might change (effectively, enabling or disabling reporting),
* the previously selected startblock will be kept.
* ----------------
*/
void
heap_rescan_set_params(HeapScanDesc scan, ScanKey key,
bool allow_strat, bool allow_sync, bool allow_pagemode)
{
/* adjust parameters */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
scan->rs_pageatatime = allow_pagemode && IsMVCCSnapshot(scan->rs_snapshot);
/* ... and rescan */
heap_rescan(scan, key);
}
/* ----------------
* heap_endscan - end relation scan
*
......
#-------------------------------------------------------------------------
#
# Makefile--
# Makefile for utils/tablesample
# Makefile for access/tablesample
#
# IDENTIFICATION
# src/backend/utils/tablesample/Makefile
# src/backend/access/tablesample/Makefile
#
#-------------------------------------------------------------------------
......@@ -12,6 +12,6 @@ subdir = src/backend/access/tablesample
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = tablesample.o system.o bernoulli.o
OBJS = bernoulli.o system.o tablesample.o
include $(top_srcdir)/src/backend/common.mk
This diff is collapsed.
This diff is collapsed.
......@@ -40,8 +40,9 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_ts_parser.h pg_ts_template.h pg_extension.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_policy.h pg_replication_origin.h \
pg_tablesample_method.h pg_default_acl.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_range.h pg_transform.h toasting.h indexing.h \
pg_default_acl.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_range.h pg_transform.h \
toasting.h indexing.h \
)
# location of Catalog.pm
......
......@@ -1911,6 +1911,14 @@ find_expr_references_walker(Node *node,
context->addrs);
}
}
else if (IsA(node, TableSampleClause))
{
TableSampleClause *tsc = (TableSampleClause *) node;
add_object_address(OCLASS_PROC, tsc->tsmhandler, 0,
context->addrs);
/* fall through to examine arguments */
}
return expression_tree_walker(node, find_expr_references_walker,
(void *) context);
......
......@@ -96,6 +96,8 @@ static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
List *ancestors, ExplainState *es);
static void show_sortorder_options(StringInfo buf, Node *sortexpr,
Oid sortOperator, Oid collation, bool nullsFirst);
static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
List *ancestors, ExplainState *es);
static void show_sort_info(SortState *sortstate, ExplainState *es);
static void show_hash_info(HashState *hashstate, ExplainState *es);
static void show_tidbitmap_info(BitmapHeapScanState *planstate,
......@@ -116,7 +118,7 @@ static void ExplainMemberNodes(List *plans, PlanState **planstates,
static void ExplainSubPlans(List *plans, List *ancestors,
const char *relationship, ExplainState *es);
static void ExplainCustomChildren(CustomScanState *css,
List *ancestors, ExplainState *es);
List *ancestors, ExplainState *es);
static void ExplainProperty(const char *qlabel, const char *value,
bool numeric, ExplainState *es);
static void ExplainOpenGroup(const char *objtype, const char *labelname,
......@@ -730,6 +732,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
switch (nodeTag(plan))
{
case T_SeqScan:
case T_SampleScan:
case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan:
......@@ -739,7 +742,6 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_SampleScan:
*rels_used = bms_add_member(*rels_used,
((Scan *) plan)->scanrelid);
break;
......@@ -935,6 +937,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_SeqScan:
pname = sname = "Seq Scan";
break;
case T_SampleScan:
pname = sname = "Sample Scan";
break;
case T_IndexScan:
pname = sname = "Index Scan";
break;
......@@ -976,23 +981,6 @@ ExplainNode(PlanState *planstate, List *ancestors,
else
pname = sname;
break;
case T_SampleScan:
{
/*
* Fetch the tablesample method name from RTE.
*
* It would be nice to also show parameters, but since we
* support arbitrary expressions as parameter it might get
* quite messy.
*/
RangeTblEntry *rte;
rte = rt_fetch(((SampleScan *) plan)->scanrelid, es->rtable);
custom_name = get_tablesample_method_name(rte->tablesample->tsmid);
pname = psprintf("Sample Scan (%s)", custom_name);
sname = "Sample Scan";
}
break;
case T_Material:
pname = sname = "Materialize";
break;
......@@ -1101,6 +1089,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
switch (nodeTag(plan))
{
case T_SeqScan:
case T_SampleScan:
case T_BitmapHeapScan:
case T_TidScan:
case T_SubqueryScan:
......@@ -1115,9 +1104,6 @@ ExplainNode(PlanState *planstate, List *ancestors,
if (((Scan *) plan)->scanrelid > 0)
ExplainScanTarget((Scan *) plan, es);
break;
case T_SampleScan:
ExplainScanTarget((Scan *) plan, es);
break;
case T_IndexScan:
{
IndexScan *indexscan = (IndexScan *) plan;
......@@ -1363,12 +1349,15 @@ ExplainNode(PlanState *planstate, List *ancestors,
if (es->analyze)
show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
break;
case T_SampleScan:
show_tablesample(((SampleScan *) plan)->tablesample,
planstate, ancestors, es);
/* FALL THRU to print additional fields the same as SeqScan */
case T_SeqScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_SubqueryScan:
case T_SampleScan:
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
......@@ -2109,6 +2098,72 @@ show_sortorder_options(StringInfo buf, Node *sortexpr,
}
}
/*
* Show TABLESAMPLE properties
*/
static void
show_tablesample(TableSampleClause *tsc, PlanState *planstate,
List *ancestors, ExplainState *es)
{
List *context;
bool useprefix;
char *method_name;
List *params = NIL;
char *repeatable;
ListCell *lc;
/* Set up deparsing context */
context = set_deparse_context_planstate(es->deparse_cxt,
(Node *) planstate,
ancestors);
useprefix = list_length(es->rtable) > 1;
/* Get the tablesample method name */
method_name = get_func_name(tsc->tsmhandler);
/* Deparse parameter expressions */
foreach(lc, tsc->args)
{
Node *arg = (Node *) lfirst(lc);
params = lappend(params,
deparse_expression(arg, context,
useprefix, false));
}
if (tsc->repeatable)
repeatable = deparse_expression((Node *) tsc->repeatable, context,
useprefix, false);
else
repeatable = NULL;
/* Print results */
if (es->format == EXPLAIN_FORMAT_TEXT)
{
bool first = true;
appendStringInfoSpaces(es->str, es->indent * 2);
appendStringInfo(es->str, "Sampling: %s (", method_name);
foreach(lc, params)
{
if (!first)
appendStringInfoString(es->str, ", ");
appendStringInfoString(es->str, (const char *) lfirst(lc));
first = false;
}
appendStringInfoChar(es->str, ')');
if (repeatable)
appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
appendStringInfoChar(es->str, '\n');
}
else
{
ExplainPropertyText("Sampling Method", method_name, es);
ExplainPropertyList("Sampling Parameters", params, es);
if (repeatable)
ExplainPropertyText("Repeatable Seed", repeatable, es);
}
}
/*
* If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
*/
......@@ -2366,13 +2421,13 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
switch (nodeTag(plan))
{
case T_SeqScan:
case T_SampleScan:
case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan:
case T_TidScan:
case T_ForeignScan:
case T_CustomScan:
case T_SampleScan:
case T_ModifyTable:
/* Assert it's on a real relation */
Assert(rte->rtekind == RTE_RELATION);
......@@ -2663,9 +2718,9 @@ ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
{
ListCell *cell;
const char *label =
(list_length(css->custom_ps) != 1 ? "children" : "child");
(list_length(css->custom_ps) != 1 ? "children" : "child");
foreach (cell, css->custom_ps)
foreach(cell, css->custom_ps)
ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
}
......
......@@ -463,6 +463,10 @@ ExecSupportsBackwardScan(Plan *node)
case T_CteScan:
return TargetListSupportsBackwardScan(node->targetlist);
case T_SampleScan:
/* Simplify life for tablesample methods by disallowing this */
return false;
case T_IndexScan:
return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
TargetListSupportsBackwardScan(node->targetlist);
......@@ -485,9 +489,6 @@ ExecSupportsBackwardScan(Plan *node)
}
return false;
case T_SampleScan:
return false;
case T_Material:
case T_Sort:
/* these don't evaluate tlist */
......
This diff is collapsed.
......@@ -359,6 +359,27 @@ _copySeqScan(const SeqScan *from)
return newnode;
}
/*
* _copySampleScan
*/
static SampleScan *
_copySampleScan(const SampleScan *from)
{
SampleScan *newnode = makeNode(SampleScan);
/*
* copy node superclass fields
*/
CopyScanFields((const Scan *) from, (Scan *) newnode);
/*
* copy remainder of node
*/
COPY_NODE_FIELD(tablesample);
return newnode;
}
/*
* _copyIndexScan
*/
......@@ -641,22 +662,6 @@ _copyCustomScan(const CustomScan *from)
return newnode;
}
/*
* _copySampleScan
*/
static SampleScan *
_copySampleScan(const SampleScan *from)
{
SampleScan *newnode = makeNode(SampleScan);
/*
* copy node superclass fields
*/
CopyScanFields((const Scan *) from, (Scan *) newnode);
return newnode;
}
/*
* CopyJoinFields
*
......@@ -2143,6 +2148,18 @@ _copyRangeTblFunction(const RangeTblFunction *from)
return newnode;
}
static TableSampleClause *
_copyTableSampleClause(const TableSampleClause *from)
{
TableSampleClause *newnode = makeNode(TableSampleClause);
COPY_SCALAR_FIELD(tsmhandler);
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(repeatable);
return newnode;
}
static WithCheckOption *
_copyWithCheckOption(const WithCheckOption *from)
{
......@@ -2271,40 +2288,6 @@ _copyCommonTableExpr(const CommonTableExpr *from)
return newnode;
}
static RangeTableSample *
_copyRangeTableSample(const RangeTableSample *from)
{
RangeTableSample *newnode = makeNode(RangeTableSample);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(method);
COPY_NODE_FIELD(repeatable);
COPY_NODE_FIELD(args);
return newnode;
}
static TableSampleClause *
_copyTableSampleClause(const TableSampleClause *from)
{
TableSampleClause *newnode = makeNode(TableSampleClause);
COPY_SCALAR_FIELD(tsmid);
COPY_SCALAR_FIELD(tsmseqscan);
COPY_SCALAR_FIELD(tsmpagemode);
COPY_SCALAR_FIELD(tsminit);
COPY_SCALAR_FIELD(tsmnextblock);
COPY_SCALAR_FIELD(tsmnexttuple);
COPY_SCALAR_FIELD(tsmexaminetuple);
COPY_SCALAR_FIELD(tsmend);
COPY_SCALAR_FIELD(tsmreset);
COPY_SCALAR_FIELD(tsmcost);
COPY_NODE_FIELD(repeatable);
COPY_NODE_FIELD(args);
return newnode;
}
static A_Expr *
_copyAExpr(const A_Expr *from)
{
......@@ -2532,6 +2515,20 @@ _copyRangeFunction(const RangeFunction *from)
return newnode;
}
static RangeTableSample *
_copyRangeTableSample(const RangeTableSample *from)
{
RangeTableSample *newnode = makeNode(RangeTableSample);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(method);
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(repeatable);
COPY_LOCATION_FIELD(location);
return newnode;
}
static TypeCast *
_copyTypeCast(const TypeCast *from)
{
......@@ -4237,6 +4234,9 @@ copyObject(const void *from)
case T_SeqScan:
retval = _copySeqScan(from);
break;
case T_SampleScan:
retval = _copySampleScan(from);
break;
case T_IndexScan:
retval = _copyIndexScan(from);
break;
......@@ -4273,9 +4273,6 @@ copyObject(const void *from)
case T_CustomScan:
retval = _copyCustomScan(from);
break;
case T_SampleScan:
retval = _copySampleScan(from);
break;
case T_Join:
retval = _copyJoin(from);
break;
......@@ -4897,6 +4894,9 @@ copyObject(const void *from)
case T_RangeFunction:
retval = _copyRangeFunction(from);
break;
case T_RangeTableSample:
retval = _copyRangeTableSample(from);
break;
case T_TypeName:
retval = _copyTypeName(from);
break;
......@@ -4921,6 +4921,9 @@ copyObject(const void *from)
case T_RangeTblFunction:
retval = _copyRangeTblFunction(from);
break;
case T_TableSampleClause:
retval = _copyTableSampleClause(from);
break;
case T_WithCheckOption:
retval = _copyWithCheckOption(from);
break;
......@@ -4948,12 +4951,6 @@ copyObject(const void *from)
case T_CommonTableExpr:
retval = _copyCommonTableExpr(from);
break;
case T_RangeTableSample:
retval = _copyRangeTableSample(from);
break;
case T_TableSampleClause:
retval = _copyTableSampleClause(from);
break;
case T_FuncWithArgs:
retval = _copyFuncWithArgs(from);
break;
......
......@@ -2290,6 +2290,18 @@ _equalRangeFunction(const RangeFunction *a, const RangeFunction *b)
return true;
}
static bool
_equalRangeTableSample(const RangeTableSample *a, const RangeTableSample *b)
{
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(method);
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(repeatable);
COMPARE_LOCATION_FIELD(location);
return true;
}
static bool
_equalIndexElem(const IndexElem *a, const IndexElem *b)
{
......@@ -2428,6 +2440,16 @@ _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b)
return true;
}
static bool
_equalTableSampleClause(const TableSampleClause *a, const TableSampleClause *b)
{
COMPARE_SCALAR_FIELD(tsmhandler);
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(repeatable);
return true;
}
static bool
_equalWithCheckOption(const WithCheckOption *a, const WithCheckOption *b)
{
......@@ -2538,36 +2560,6 @@ _equalCommonTableExpr(const CommonTableExpr *a, const CommonTableExpr *b)
return true;
}
static bool
_equalRangeTableSample(const RangeTableSample *a, const RangeTableSample *b)
{
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(method);
COMPARE_NODE_FIELD(repeatable);
COMPARE_NODE_FIELD(args);
return true;
}
static bool
_equalTableSampleClause(const TableSampleClause *a, const TableSampleClause *b)
{
COMPARE_SCALAR_FIELD(tsmid);
COMPARE_SCALAR_FIELD(tsmseqscan);
COMPARE_SCALAR_FIELD(tsmpagemode);
COMPARE_SCALAR_FIELD(tsminit);
COMPARE_SCALAR_FIELD(tsmnextblock);
COMPARE_SCALAR_FIELD(tsmnexttuple);
COMPARE_SCALAR_FIELD(tsmexaminetuple);
COMPARE_SCALAR_FIELD(tsmend);
COMPARE_SCALAR_FIELD(tsmreset);
COMPARE_SCALAR_FIELD(tsmcost);
COMPARE_NODE_FIELD(repeatable);
COMPARE_NODE_FIELD(args);
return true;
}
static bool
_equalXmlSerialize(const XmlSerialize *a, const XmlSerialize *b)
{
......@@ -3260,6 +3252,9 @@ equal(const void *a, const void *b)
case T_RangeFunction:
retval = _equalRangeFunction(a, b);
break;
case T_RangeTableSample:
retval = _equalRangeTableSample(a, b);
break;
case T_TypeName:
retval = _equalTypeName(a, b);
break;
......@@ -3284,6 +3279,9 @@ equal(const void *a, const void *b)
case T_RangeTblFunction:
retval = _equalRangeTblFunction(a, b);
break;
case T_TableSampleClause:
retval = _equalTableSampleClause(a, b);
break;
case T_WithCheckOption:
retval = _equalWithCheckOption(a, b);
break;
......@@ -3311,12 +3309,6 @@ equal(const void *a, const void *b)
case T_CommonTableExpr:
retval = _equalCommonTableExpr(a, b);
break;
case T_RangeTableSample:
retval = _equalRangeTableSample(a, b);
break;
case T_TableSampleClause:
retval = _equalTableSampleClause(a, b);
break;
case T_FuncWithArgs:
retval = _equalFuncWithArgs(a, b);
break;
......
......@@ -1486,6 +1486,9 @@ exprLocation(const Node *expr)
case T_WindowDef:
loc = ((const WindowDef *) expr)->location;
break;
case T_RangeTableSample:
loc = ((const RangeTableSample *) expr)->location;
break;
case T_TypeName:
loc = ((const TypeName *) expr)->location;
break;
......@@ -1995,6 +1998,17 @@ expression_tree_walker(Node *node,
return walker(((PlaceHolderInfo *) node)->ph_var, context);
case T_RangeTblFunction:
return walker(((RangeTblFunction *) node)->funcexpr, context);
case T_TableSampleClause:
{
TableSampleClause *tsc = (TableSampleClause *) node;
if (expression_tree_walker((Node *) tsc->args,
walker, context))
return true;
if (walker((Node *) tsc->repeatable, context))
return true;
}
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
......@@ -2082,13 +2096,8 @@ range_table_walker(List *rtable,
switch (rte->rtekind)
{
case RTE_RELATION:
if (rte->tablesample)
{
if (walker(rte->tablesample->args, context))
return true;
if (walker(rte->tablesample->repeatable, context))
return true;
}
if (walker(rte->tablesample, context))
return true;
break;
case RTE_CTE:
/* nothing to do */
......@@ -2782,6 +2791,17 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_TableSampleClause:
{
TableSampleClause *tsc = (TableSampleClause *) node;
TableSampleClause *newnode;
FLATCOPY(newnode, tsc, TableSampleClause);
MUTATE(newnode->args, tsc->args, List *);
MUTATE(newnode->repeatable, tsc->repeatable, Expr *);
return (Node *) newnode;
}
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
......@@ -2868,20 +2888,12 @@ range_table_mutator(List *rtable,
switch (rte->rtekind)
{
case RTE_RELATION:
if (rte->tablesample)
{
CHECKFLATCOPY(newrte->tablesample, rte->tablesample,
TableSampleClause);
MUTATE(newrte->tablesample->args,
newrte->tablesample->args,
List *);
MUTATE(newrte->tablesample->repeatable,
newrte->tablesample->repeatable,
Node *);
}
MUTATE(newrte->tablesample, rte->tablesample,
TableSampleClause *);
/* we don't bother to copy eref, aliases, etc; OK? */
break;
case RTE_CTE:
/* we don't bother to copy eref, aliases, etc; OK? */
/* nothing to do */
break;
case RTE_SUBQUERY:
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
......@@ -3316,6 +3328,19 @@ raw_expression_tree_walker(Node *node,
return true;
}
break;
case T_RangeTableSample:
{
RangeTableSample *rts = (RangeTableSample *) node;
if (walker(rts->relation, context))
return true;
/* method name is deemed uninteresting */
if (walker(rts->args, context))
return true;
if (walker(rts->repeatable, context))
return true;
}
break;
case T_TypeName:
{
TypeName *tn = (TypeName *) node;
......@@ -3380,18 +3405,6 @@ raw_expression_tree_walker(Node *node,
break;
case T_CommonTableExpr:
return walker(((CommonTableExpr *) node)->ctequery, context);
case T_RangeTableSample:
{
RangeTableSample *rts = (RangeTableSample *) node;
if (walker(rts->relation, context))
return true;
if (walker(rts->repeatable, context))
return true;
if (walker(rts->args, context))
return true;
}
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
......
This diff is collapsed.
......@@ -367,46 +367,6 @@ _readCommonTableExpr(void)
READ_DONE();
}
/*
* _readRangeTableSample
*/
static RangeTableSample *
_readRangeTableSample(void)
{
READ_LOCALS(RangeTableSample);
READ_NODE_FIELD(relation);
READ_STRING_FIELD(method);
READ_NODE_FIELD(repeatable);
READ_NODE_FIELD(args);
READ_DONE();
}
/*
* _readTableSampleClause
*/
static TableSampleClause *
_readTableSampleClause(void)
{
READ_LOCALS(TableSampleClause);
READ_OID_FIELD(tsmid);
READ_BOOL_FIELD(tsmseqscan);
READ_BOOL_FIELD(tsmpagemode);
READ_OID_FIELD(tsminit);
READ_OID_FIELD(tsmnextblock);
READ_OID_FIELD(tsmnexttuple);
READ_OID_FIELD(tsmexaminetuple);
READ_OID_FIELD(tsmend);
READ_OID_FIELD(tsmreset);
READ_OID_FIELD(tsmcost);
READ_NODE_FIELD(repeatable);
READ_NODE_FIELD(args);
READ_DONE();
}
/*
* _readSetOperationStmt
*/
......@@ -1391,6 +1351,21 @@ _readRangeTblFunction(void)
READ_DONE();
}
/*
* _readTableSampleClause
*/
static TableSampleClause *
_readTableSampleClause(void)
{
READ_LOCALS(TableSampleClause);
READ_OID_FIELD(tsmhandler);
READ_NODE_FIELD(args);
READ_NODE_FIELD(repeatable);
READ_DONE();
}
/*
* parseNodeString
......@@ -1426,10 +1401,6 @@ parseNodeString(void)
return_value = _readRowMarkClause();
else if (MATCH("COMMONTABLEEXPR", 15))
return_value = _readCommonTableExpr();
else if (MATCH("RANGETABLESAMPLE", 16))
return_value = _readRangeTableSample();
else if (MATCH("TABLESAMPLECLAUSE", 17))
return_value = _readTableSampleClause();
else if (MATCH("SETOPERATIONSTMT", 16))
return_value = _readSetOperationStmt();
else if (MATCH("ALIAS", 5))
......@@ -1528,6 +1499,8 @@ parseNodeString(void)
return_value = _readRangeTblEntry();
else if (MATCH("RANGETBLFUNCTION", 16))
return_value = _readRangeTblFunction();
else if (MATCH("TABLESAMPLECLAUSE", 17))
return_value = _readTableSampleClause();
else if (MATCH("NOTIFY", 6))
return_value = _readNotifyStmt();
else if (MATCH("DECLARECURSOR", 13))
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment