PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pgstatapprox.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pgstatapprox.c
4  * Bloat estimation functions
5  *
6  * Copyright (c) 2014-2017, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  * contrib/pgstattuple/pgstatapprox.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14 
15 #include "access/visibilitymap.h"
16 #include "access/transam.h"
17 #include "access/xact.h"
18 #include "access/multixact.h"
19 #include "access/htup_details.h"
20 #include "catalog/namespace.h"
21 #include "funcapi.h"
22 #include "miscadmin.h"
23 #include "storage/bufmgr.h"
24 #include "storage/freespace.h"
25 #include "storage/procarray.h"
26 #include "storage/lmgr.h"
27 #include "utils/builtins.h"
28 #include "utils/tqual.h"
29 #include "commands/vacuum.h"
30 
33 
35 
36 typedef struct output_type
37 {
38  uint64 table_len;
40  uint64 tuple_count;
41  uint64 tuple_len;
42  double tuple_percent;
46  uint64 free_space;
47  double free_percent;
48 } output_type;
49 
50 #define NUM_OUTPUT_COLUMNS 10
51 
52 /*
53  * This function takes an already open relation and scans its pages,
54  * skipping those that have the corresponding visibility map bit set.
55  * For pages we skip, we find the free space from the free space map
56  * and approximate tuple_len on that basis. For the others, we count
57  * the exact number of dead tuples etc.
58  *
59  * This scan is loosely based on vacuumlazy.c:lazy_scan_heap(), but
60  * we do not try to avoid skipping single pages.
61  */
62 static void
64 {
65  BlockNumber scanned,
66  nblocks,
67  blkno;
68  Buffer vmbuffer = InvalidBuffer;
69  BufferAccessStrategy bstrategy;
71  uint64 misc_count = 0;
72 
73  OldestXmin = GetOldestXmin(rel, true);
74  bstrategy = GetAccessStrategy(BAS_BULKREAD);
75 
76  nblocks = RelationGetNumberOfBlocks(rel);
77  scanned = 0;
78 
79  for (blkno = 0; blkno < nblocks; blkno++)
80  {
81  Buffer buf;
82  Page page;
83  OffsetNumber offnum,
84  maxoff;
85  Size freespace;
86 
88 
89  /*
90  * If the page has only visible tuples, then we can find out the free
91  * space from the FSM and move on.
92  */
93  if (VM_ALL_VISIBLE(rel, blkno, &vmbuffer))
94  {
95  freespace = GetRecordedFreeSpace(rel, blkno);
96  stat->tuple_len += BLCKSZ - freespace;
97  stat->free_space += freespace;
98  continue;
99  }
100 
101  buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
102  RBM_NORMAL, bstrategy);
103 
105 
106  page = BufferGetPage(buf);
107 
108  /*
109  * It's not safe to call PageGetHeapFreeSpace() on new pages, so we
110  * treat them as being free space for our purposes.
111  */
112  if (!PageIsNew(page))
113  stat->free_space += PageGetHeapFreeSpace(page);
114  else
115  stat->free_space += BLCKSZ - SizeOfPageHeaderData;
116 
117  if (PageIsNew(page) || PageIsEmpty(page))
118  {
119  UnlockReleaseBuffer(buf);
120  continue;
121  }
122 
123  scanned++;
124 
125  /*
126  * Look at each tuple on the page and decide whether it's live or
127  * dead, then count it and its size. Unlike lazy_scan_heap, we can
128  * afford to ignore problems and special cases.
129  */
130  maxoff = PageGetMaxOffsetNumber(page);
131 
132  for (offnum = FirstOffsetNumber;
133  offnum <= maxoff;
134  offnum = OffsetNumberNext(offnum))
135  {
136  ItemId itemid;
137  HeapTupleData tuple;
138 
139  itemid = PageGetItemId(page, offnum);
140 
141  if (!ItemIdIsUsed(itemid) || ItemIdIsRedirected(itemid) ||
142  ItemIdIsDead(itemid))
143  {
144  continue;
145  }
146 
147  Assert(ItemIdIsNormal(itemid));
148 
149  ItemPointerSet(&(tuple.t_self), blkno, offnum);
150 
151  tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
152  tuple.t_len = ItemIdGetLength(itemid);
153  tuple.t_tableOid = RelationGetRelid(rel);
154 
155  /*
156  * We count live and dead tuples, but we also need to add up
157  * others in order to feed vac_estimate_reltuples.
158  */
159  switch (HeapTupleSatisfiesVacuum(&tuple, OldestXmin, buf))
160  {
162  misc_count++;
163  /* Fall through */
164  case HEAPTUPLE_DEAD:
165  stat->dead_tuple_len += tuple.t_len;
166  stat->dead_tuple_count++;
167  break;
168  case HEAPTUPLE_LIVE:
169  stat->tuple_len += tuple.t_len;
170  stat->tuple_count++;
171  break;
174  misc_count++;
175  break;
176  default:
177  elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result");
178  break;
179  }
180  }
181 
182  UnlockReleaseBuffer(buf);
183  }
184 
185  stat->table_len = (uint64) nblocks *BLCKSZ;
186 
187  stat->tuple_count = vac_estimate_reltuples(rel, false, nblocks, scanned,
188  stat->tuple_count + misc_count);
189 
190  /*
191  * Calculate percentages if the relation has one or more pages.
192  */
193  if (nblocks != 0)
194  {
195  stat->scanned_percent = 100 * scanned / nblocks;
196  stat->tuple_percent = 100.0 * stat->tuple_len / stat->table_len;
197  stat->dead_tuple_percent = 100.0 * stat->dead_tuple_len / stat->table_len;
198  stat->free_percent = 100.0 * stat->free_space / stat->table_len;
199  }
200 
201  if (BufferIsValid(vmbuffer))
202  {
203  ReleaseBuffer(vmbuffer);
204  vmbuffer = InvalidBuffer;
205  }
206 }
207 
208 /*
209  * Returns estimated live/dead tuple statistics for the given relid.
210  *
211  * The superuser() check here must be kept as the library might be upgraded
212  * without the extension being upgraded, meaning that in pre-1.5 installations
213  * these functions could be called by any user.
214  */
215 Datum
217 {
218  Oid relid = PG_GETARG_OID(0);
219 
220  if (!superuser())
221  ereport(ERROR,
222  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
223  (errmsg("must be superuser to use pgstattuple functions"))));
224 
226 }
227 
228 /*
229  * As of pgstattuple version 1.5, we no longer need to check if the user
230  * is a superuser because we REVOKE EXECUTE on the SQL function from PUBLIC.
231  * Users can then grant access to it based on their policies.
232  *
233  * Otherwise identical to pgstattuple_approx (above).
234  */
235 Datum
237 {
238  Oid relid = PG_GETARG_OID(0);
239 
241 }
242 
243 Datum
245 {
246  Relation rel;
247  output_type stat = {0};
248  TupleDesc tupdesc;
249  bool nulls[NUM_OUTPUT_COLUMNS];
251  HeapTuple ret;
252  int i = 0;
253 
254  if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
255  elog(ERROR, "return type must be a row type");
256 
257  if (tupdesc->natts != NUM_OUTPUT_COLUMNS)
258  elog(ERROR, "incorrect number of output arguments");
259 
260  rel = relation_open(relid, AccessShareLock);
261 
262  /*
263  * Reject attempts to read non-local temporary relations; we would be
264  * likely to get wrong data since we have no visibility into the owning
265  * session's local buffers.
266  */
267  if (RELATION_IS_OTHER_TEMP(rel))
268  ereport(ERROR,
269  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
270  errmsg("cannot access temporary tables of other sessions")));
271 
272  /*
273  * We support only ordinary relations and materialised views, because we
274  * depend on the visibility map and free space map for our estimates about
275  * unscanned pages.
276  */
277  if (!(rel->rd_rel->relkind == RELKIND_RELATION ||
278  rel->rd_rel->relkind == RELKIND_MATVIEW))
279  ereport(ERROR,
280  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
281  errmsg("\"%s\" is not a table or materialized view",
282  RelationGetRelationName(rel))));
283 
284  statapprox_heap(rel, &stat);
285 
287 
288  memset(nulls, 0, sizeof(nulls));
289 
290  values[i++] = Int64GetDatum(stat.table_len);
291  values[i++] = Float8GetDatum(stat.scanned_percent);
292  values[i++] = Int64GetDatum(stat.tuple_count);
293  values[i++] = Int64GetDatum(stat.tuple_len);
294  values[i++] = Float8GetDatum(stat.tuple_percent);
295  values[i++] = Int64GetDatum(stat.dead_tuple_count);
296  values[i++] = Int64GetDatum(stat.dead_tuple_len);
297  values[i++] = Float8GetDatum(stat.dead_tuple_percent);
298  values[i++] = Int64GetDatum(stat.free_space);
299  values[i++] = Float8GetDatum(stat.free_percent);
300 
301  ret = heap_form_tuple(tupdesc, values, nulls);
302  return HeapTupleGetDatum(ret);
303 }
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:525
#define NUM_OUTPUT_COLUMNS
Definition: pgstatapprox.c:50
uint64 dead_tuple_count
Definition: pgstatapprox.c:43
struct output_type output_type
#define PageIsEmpty(page)
Definition: bufpage.h:219
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:211
Datum pgstattuple_approx(PG_FUNCTION_ARGS)
Definition: pgstatapprox.c:216
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:105
double vac_estimate_reltuples(Relation relation, bool is_analyze, BlockNumber total_pages, BlockNumber scanned_pages, double scanned_tuples)
Definition: vacuum.c:655
uint32 TransactionId
Definition: c.h:393
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:640
#define ItemIdIsUsed(itemId)
Definition: itemid.h:91
#define RELKIND_MATVIEW
Definition: pg_class.h:167
#define AccessShareLock
Definition: lockdefs.h:36
#define InvalidBuffer
Definition: buf.h:25
HTSV_Result HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin, Buffer buffer)
Definition: tqual.c:1164
int errcode(int sqlerrcode)
Definition: elog.c:575
bool superuser(void)
Definition: superuser.c:47
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1263
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3292
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:692
double dead_tuple_percent
Definition: pgstatapprox.c:45
#define SizeOfPageHeaderData
Definition: bufpage.h:213
Form_pg_class rd_rel
Definition: rel.h:113
unsigned int Oid
Definition: postgres_ext.h:31
#define ItemIdIsDead(itemId)
Definition: itemid.h:112
uint64 tuple_count
Definition: pgstatapprox.c:40
double free_percent
Definition: pgstatapprox.c:47
uint64 table_len
Definition: pgstatapprox.c:38
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:354
int natts
Definition: tupdesc.h:73
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:2126
uint16 OffsetNumber
Definition: off.h:24
HeapTupleHeader t_data
Definition: htup.h:67
double tuple_percent
Definition: pgstatapprox.c:42
uint64 tuple_len
Definition: pgstatapprox.c:41
Datum pgstattuple_approx_v1_5(PG_FUNCTION_ARGS)
Definition: pgstatapprox.c:236
#define ItemIdGetLength(itemId)
Definition: itemid.h:58
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3315
#define ERROR
Definition: elog.h:43
Size PageGetHeapFreeSpace(Page page)
Definition: bufpage.c:639
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
static char * buf
Definition: pg_test_fsync.c:65
#define PG_GETARG_OID(n)
Definition: fmgr.h:231
#define FirstOffsetNumber
Definition: off.h:27
#define RelationGetRelationName(relation)
Definition: rel.h:433
static TransactionId OldestXmin
Definition: vacuumlazy.c:138
Oid t_tableOid
Definition: htup.h:66
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:2102
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
Size GetRecordedFreeSpace(Relation rel, BlockNumber heapBlk)
Definition: freespace.c:270
PG_FUNCTION_INFO_V1(pgstattuple_approx)
uintptr_t Datum
Definition: postgres.h:374
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:297
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3529
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:199
TransactionId GetOldestXmin(Relation rel, bool ignoreVacuum)
Definition: procarray.c:1305
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:670
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:530
#define ItemIdIsNormal(itemId)
Definition: itemid.h:98
#define VM_ALL_VISIBLE(r, b, v)
Definition: visibilitymap.h:32
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
size_t Size
Definition: c.h:352
Datum pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo)
Definition: pgstatapprox.c:244
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
#define HeapTupleGetDatum(tuple)
Definition: funcapi.h:222
static void statapprox_heap(Relation rel, output_type *stat)
Definition: pgstatapprox.c:63
static Datum values[MAXATTR]
Definition: bootstrap.c:162
uint64 free_space
Definition: pgstatapprox.c:46
#define PageIsNew(page)
Definition: bufpage.h:226
uint64 dead_tuple_len
Definition: pgstatapprox.c:44
int errmsg(const char *fmt,...)
Definition: elog.c:797
uint64 scanned_percent
Definition: pgstatapprox.c:39
int i
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:88
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1117
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:97
#define elog
Definition: elog.h:219
#define RELKIND_RELATION
Definition: pg_class.h:160
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:413
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:86