Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4d40494

Browse files
contrib/tsm_system_rows
1 parent 149f6f1 commit 4d40494

File tree

7 files changed

+390
-0
lines changed

7 files changed

+390
-0
lines changed

contrib/tsm_system_rows/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Generated subdirectories
2+
/log/
3+
/results/
4+
/tmp_check/

contrib/tsm_system_rows/Makefile

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# src/test/modules/tsm_system_rows/Makefile
2+
3+
MODULE_big = tsm_system_rows
4+
OBJS = tsm_system_rows.o $(WIN32RES)
5+
PGFILEDESC = "tsm_system_rows - SYSTEM TABLESAMPLE method which accepts number of rows as a limit"
6+
7+
EXTENSION = tsm_system_rows
8+
DATA = tsm_system_rows--1.0.sql
9+
10+
REGRESS = tsm_system_rows
11+
12+
ifdef USE_PGXS
13+
PG_CONFIG = pg_config
14+
PGXS := $(shell $(PG_CONFIG) --pgxs)
15+
include $(PGXS)
16+
else
17+
subdir = contrib/tsm_system_rows
18+
top_builddir = ../..
19+
include $(top_builddir)/src/Makefile.global
20+
include $(top_srcdir)/contrib/contrib-global.mk
21+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
CREATE EXTENSION tsm_system_rows;
2+
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
3+
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000) FROM generate_series(0, 30) s(i) ORDER BY i;
4+
ANALYZE test_tablesample;
5+
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (1000);
6+
count
7+
-------
8+
31
9+
(1 row)
10+
11+
SELECT id FROM test_tablesample TABLESAMPLE system_rows (8) REPEATABLE (5432);
12+
id
13+
----
14+
7
15+
14
16+
21
17+
28
18+
4
19+
11
20+
18
21+
25
22+
(8 rows)
23+
24+
EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE system_rows (20) REPEATABLE (10);
25+
QUERY PLAN
26+
-----------------------------------------------------------------------------------
27+
Sample Scan (system_rows) on test_tablesample (cost=0.00..80.20 rows=20 width=4)
28+
(1 row)
29+
30+
-- done
31+
DROP TABLE test_tablesample CASCADE;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CREATE EXTENSION tsm_system_rows;
2+
3+
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
4+
5+
INSERT INTO test_tablesample SELECT i, repeat(i::text, 1000) FROM generate_series(0, 30) s(i) ORDER BY i;
6+
ANALYZE test_tablesample;
7+
8+
SELECT count(*) FROM test_tablesample TABLESAMPLE system_rows (1000);
9+
SELECT id FROM test_tablesample TABLESAMPLE system_rows (8) REPEATABLE (5432);
10+
11+
EXPLAIN SELECT id FROM test_tablesample TABLESAMPLE system_rows (20) REPEATABLE (10);
12+
13+
-- done
14+
DROP TABLE test_tablesample CASCADE;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* src/test/modules/tablesample/tsm_system_rows--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION tsm_system_rows" to load this file. \quit
5+
6+
CREATE FUNCTION tsm_system_rows_init(internal, int4, int4)
7+
RETURNS void
8+
AS 'MODULE_PATHNAME'
9+
LANGUAGE C STRICT;
10+
11+
CREATE FUNCTION tsm_system_rows_nextblock(internal)
12+
RETURNS int4
13+
AS 'MODULE_PATHNAME'
14+
LANGUAGE C STRICT;
15+
16+
CREATE FUNCTION tsm_system_rows_nexttuple(internal, int4, int2)
17+
RETURNS int2
18+
AS 'MODULE_PATHNAME'
19+
LANGUAGE C STRICT;
20+
21+
CREATE FUNCTION tsm_system_rows_examinetuple(internal, int4, internal, bool)
22+
RETURNS bool
23+
AS 'MODULE_PATHNAME'
24+
LANGUAGE C STRICT;
25+
26+
CREATE FUNCTION tsm_system_rows_end(internal)
27+
RETURNS void
28+
AS 'MODULE_PATHNAME'
29+
LANGUAGE C STRICT;
30+
31+
CREATE FUNCTION tsm_system_rows_reset(internal)
32+
RETURNS void
33+
AS 'MODULE_PATHNAME'
34+
LANGUAGE C STRICT;
35+
36+
CREATE FUNCTION tsm_system_rows_cost(internal, internal, internal, internal, internal, internal, internal)
37+
RETURNS void
38+
AS 'MODULE_PATHNAME'
39+
LANGUAGE C STRICT;
40+
41+
INSERT INTO pg_tablesample_method VALUES('system_rows', false, true,
42+
'tsm_system_rows_init', 'tsm_system_rows_nextblock',
43+
'tsm_system_rows_nexttuple', 'tsm_system_rows_examinetuple',
44+
'tsm_system_rows_end', 'tsm_system_rows_reset', 'tsm_system_rows_cost');
45+
+270
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* tsm_system_rows.c
4+
* interface routines for system_rows tablesample method
5+
*
6+
*
7+
* Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
8+
*
9+
* IDENTIFICATION
10+
* contrib/tsm_system_rows_rowlimit/tsm_system_rows.c
11+
*
12+
*-------------------------------------------------------------------------
13+
*/
14+
15+
#include "postgres.h"
16+
17+
#include "fmgr.h"
18+
19+
#include "access/tablesample.h"
20+
#include "access/relscan.h"
21+
#include "miscadmin.h"
22+
#include "nodes/execnodes.h"
23+
#include "nodes/relation.h"
24+
#include "optimizer/clauses.h"
25+
#include "storage/bufmgr.h"
26+
#include "utils/sampling.h"
27+
28+
PG_MODULE_MAGIC;
29+
30+
/*
31+
* State
32+
*/
33+
typedef struct
34+
{
35+
SamplerRandomState randstate;
36+
uint32 seed; /* random seed */
37+
BlockNumber nblocks; /* number of block in relation */
38+
int32 ntuples; /* number of tuples to return */
39+
int32 donetuples; /* tuples already returned */
40+
OffsetNumber lt; /* last tuple returned from current block */
41+
BlockNumber step; /* step size */
42+
BlockNumber lb; /* last block visited */
43+
BlockNumber doneblocks; /* number of already returned blocks */
44+
} SystemSamplerData;
45+
46+
47+
PG_FUNCTION_INFO_V1(tsm_system_rows_init);
48+
PG_FUNCTION_INFO_V1(tsm_system_rows_nextblock);
49+
PG_FUNCTION_INFO_V1(tsm_system_rows_nexttuple);
50+
PG_FUNCTION_INFO_V1(tsm_system_rows_examinetuple);
51+
PG_FUNCTION_INFO_V1(tsm_system_rows_end);
52+
PG_FUNCTION_INFO_V1(tsm_system_rows_reset);
53+
PG_FUNCTION_INFO_V1(tsm_system_rows_cost);
54+
55+
static uint32 random_relative_prime(uint32 n, SamplerRandomState randstate);
56+
57+
/*
58+
* Initializes the state.
59+
*/
60+
Datum
61+
tsm_system_rows_init(PG_FUNCTION_ARGS)
62+
{
63+
TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
64+
uint32 seed = PG_GETARG_UINT32(1);
65+
int32 ntuples = PG_ARGISNULL(2) ? -1 : PG_GETARG_INT32(2);
66+
HeapScanDesc scan = tsdesc->heapScan;
67+
SystemSamplerData *sampler;
68+
69+
if (ntuples < 1)
70+
ereport(ERROR,
71+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
72+
errmsg("invalid sample size"),
73+
errhint("Sample size must be positive integer value.")));
74+
75+
sampler = palloc0(sizeof(SystemSamplerData));
76+
77+
/* Remember initial values for reinit */
78+
sampler->seed = seed;
79+
sampler->nblocks = scan->rs_nblocks;
80+
sampler->ntuples = ntuples;
81+
sampler->donetuples = 0;
82+
sampler->lt = InvalidOffsetNumber;
83+
sampler->doneblocks = 0;
84+
85+
sampler_random_init_state(sampler->seed, sampler->randstate);
86+
87+
/* Find relative prime as step size for linear probing. */
88+
sampler->step = random_relative_prime(sampler->nblocks, sampler->randstate);
89+
/*
90+
* Randomize start position so that blocks close to step size don't have
91+
* higher probability of being chosen on very short scan.
92+
*/
93+
sampler->lb = sampler_random_fract(sampler->randstate) *
94+
(sampler->nblocks / sampler->step);
95+
96+
tsdesc->tsmdata = (void *) sampler;
97+
98+
PG_RETURN_VOID();
99+
}
100+
101+
/*
102+
* Get next block number or InvalidBlockNumber when we're done.
103+
*
104+
* Uses linear probing algorithm for picking next block.
105+
*/
106+
Datum
107+
tsm_system_rows_nextblock(PG_FUNCTION_ARGS)
108+
{
109+
TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
110+
SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;
111+
112+
sampler->lb = (sampler->lb + sampler->step) % sampler->nblocks;
113+
sampler->doneblocks++;
114+
115+
/* All blocks have been read, we're done */
116+
if (sampler->doneblocks > sampler->nblocks ||
117+
sampler->donetuples >= sampler->ntuples)
118+
PG_RETURN_UINT32(InvalidBlockNumber);
119+
120+
PG_RETURN_UINT32(sampler->lb);
121+
}
122+
123+
/*
124+
* Get next tuple offset in current block or InvalidOffsetNumber if we are done
125+
* with this block.
126+
*/
127+
Datum
128+
tsm_system_rows_nexttuple(PG_FUNCTION_ARGS)
129+
{
130+
TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
131+
OffsetNumber maxoffset = PG_GETARG_UINT16(2);
132+
SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;
133+
OffsetNumber tupoffset = sampler->lt;
134+
135+
if (tupoffset == InvalidOffsetNumber)
136+
tupoffset = FirstOffsetNumber;
137+
else
138+
tupoffset++;
139+
140+
if (tupoffset > maxoffset ||
141+
sampler->donetuples >= sampler->ntuples)
142+
tupoffset = InvalidOffsetNumber;
143+
144+
sampler->lt = tupoffset;
145+
146+
PG_RETURN_UINT16(tupoffset);
147+
}
148+
149+
/*
150+
* Examine tuple and decide if it should be returned.
151+
*/
152+
Datum
153+
tsm_system_rows_examinetuple(PG_FUNCTION_ARGS)
154+
{
155+
TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
156+
bool visible = PG_GETARG_BOOL(3);
157+
SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;
158+
159+
if (!visible)
160+
PG_RETURN_BOOL(false);
161+
162+
sampler->donetuples++;
163+
164+
PG_RETURN_BOOL(true);
165+
}
166+
167+
/*
168+
* Cleanup method.
169+
*/
170+
Datum
171+
tsm_system_rows_end(PG_FUNCTION_ARGS)
172+
{
173+
TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
174+
175+
pfree(tsdesc->tsmdata);
176+
177+
PG_RETURN_VOID();
178+
}
179+
180+
/*
181+
* Reset state (called by ReScan).
182+
*/
183+
Datum
184+
tsm_system_rows_reset(PG_FUNCTION_ARGS)
185+
{
186+
TableSampleDesc *tsdesc = (TableSampleDesc *) PG_GETARG_POINTER(0);
187+
SystemSamplerData *sampler = (SystemSamplerData *) tsdesc->tsmdata;
188+
189+
sampler->lt = InvalidOffsetNumber;
190+
sampler->donetuples = 0;
191+
sampler->doneblocks = 0;
192+
193+
sampler_random_init_state(sampler->seed, sampler->randstate);
194+
sampler->step = random_relative_prime(sampler->nblocks, sampler->randstate);
195+
sampler->lb = sampler_random_fract(sampler->randstate) * (sampler->nblocks / sampler->step);
196+
197+
PG_RETURN_VOID();
198+
}
199+
200+
/*
201+
* Costing function.
202+
*/
203+
Datum
204+
tsm_system_rows_cost(PG_FUNCTION_ARGS)
205+
{
206+
PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
207+
Path *path = (Path *) PG_GETARG_POINTER(1);
208+
RelOptInfo *baserel = (RelOptInfo *) PG_GETARG_POINTER(2);
209+
List *args = (List *) PG_GETARG_POINTER(3);
210+
BlockNumber *pages = (BlockNumber *) PG_GETARG_POINTER(4);
211+
double *tuples = (double *) PG_GETARG_POINTER(5);
212+
Node *limitnode;
213+
int32 ntuples;
214+
215+
limitnode = linitial(args);
216+
limitnode = estimate_expression_value(root, limitnode);
217+
218+
if (IsA(limitnode, RelabelType))
219+
limitnode = (Node *) ((RelabelType *) limitnode)->arg;
220+
221+
if (IsA(limitnode, Const))
222+
ntuples = DatumGetInt32(((Const *) limitnode)->constvalue);
223+
else
224+
{
225+
/* Default ntuples if the estimation didn't return Const. */
226+
ntuples = 1000;
227+
}
228+
229+
*pages = Min(baserel->pages, ntuples);
230+
*tuples = ntuples;
231+
path->rows = *tuples;
232+
233+
PG_RETURN_VOID();
234+
}
235+
236+
237+
static uint32
238+
gcd (uint32 a, uint32 b)
239+
{
240+
uint32 c;
241+
242+
while (a != 0)
243+
{
244+
c = a;
245+
a = b % a;
246+
b = c;
247+
}
248+
249+
return b;
250+
}
251+
252+
static uint32
253+
random_relative_prime(uint32 n, SamplerRandomState randstate)
254+
{
255+
/* Pick random starting number, with some limits on what it can be. */
256+
uint32 r = (uint32) sampler_random_fract(randstate) * n/2 + n/4,
257+
t;
258+
259+
/*
260+
* This should only take 2 or 3 iterations as the probability of 2 numbers
261+
* being relatively prime is ~61%.
262+
*/
263+
while ((t = gcd(r, n)) > 1)
264+
{
265+
CHECK_FOR_INTERRUPTS();
266+
r /= t;
267+
}
268+
269+
return r;
270+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# tsm_system_rows extension
2+
comment = 'SYSTEM TABLESAMPLE method which accepts number rows as a limit'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/tsm_system_rows'
5+
relocatable = true

0 commit comments

Comments
 (0)