PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_buffercache_pages.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_buffercache_pages.c
4 * display some contents of the buffer cache
5 *
6 * contrib/pg_buffercache/pg_buffercache_pages.c
7 *-------------------------------------------------------------------------
8 */
9#include "postgres.h"
10
11#include "access/htup_details.h"
12#include "access/relation.h"
13#include "catalog/pg_type.h"
14#include "funcapi.h"
15#include "port/pg_numa.h"
17#include "storage/bufmgr.h"
18#include "utils/rel.h"
19
20
21#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
22#define NUM_BUFFERCACHE_PAGES_ELEM 9
23#define NUM_BUFFERCACHE_SUMMARY_ELEM 5
24#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4
25#define NUM_BUFFERCACHE_EVICT_ELEM 2
26#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3
27#define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3
28#define NUM_BUFFERCACHE_MARK_DIRTY_ELEM 2
29#define NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM 3
30#define NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM 3
31
32#define NUM_BUFFERCACHE_OS_PAGES_ELEM 3
33
35 .name = "pg_buffercache",
36 .version = PG_VERSION
37);
38
39/*
40 * Record structure holding the to be exposed cache data.
41 */
42typedef struct
43{
50 bool isvalid;
51 bool isdirty;
53
54 /*
55 * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
56 * being pinned by too many backends and each backend will only pin once
57 * because of bufmgr.c's PrivateRefCount infrastructure.
58 */
61
62
63/*
64 * Function context for data persisting over repeated calls.
65 */
71
72/*
73 * Record structure holding the to be exposed cache data for OS pages. This
74 * structure is used by pg_buffercache_os_pages(), where NUMA information may
75 * or may not be included.
76 */
83
84/*
85 * Function context for data persisting over repeated calls.
86 */
93
94
95/*
96 * Function returning data from the shared buffer cache - buffer number,
97 * relation node/tablespace/database/blocknum and dirty indicator.
98 */
110
111
112/* Only need to touch memory once per backend process lifetime */
113static bool firstNumaTouch = true;
114
115
116Datum
118{
120 Datum result;
121 MemoryContext oldcontext;
122 BufferCachePagesContext *fctx; /* User function context. */
123 TupleDesc tupledesc;
125 HeapTuple tuple;
126
127 if (SRF_IS_FIRSTCALL())
128 {
129 int i;
130
132
133 /* Switch context when allocating stuff to be used in later calls */
134 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
135
136 /* Create a user function context for cross-call persistence */
138
139 /*
140 * To smoothly support upgrades from version 1.0 of this extension
141 * transparently handle the (non-)existence of the pinning_backends
142 * column. We unfortunately have to get the result type for that... -
143 * we can't use the result type determined by the function definition
144 * without potentially crashing when somebody uses the old (or even
145 * wrong) function definition though.
146 */
148 elog(ERROR, "return type must be a row type");
149
152 elog(ERROR, "incorrect number of output arguments");
153
154 /* Construct a tuple descriptor for the result rows. */
156 TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
157 INT4OID, -1, 0);
158 TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
159 OIDOID, -1, 0);
160 TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
161 OIDOID, -1, 0);
162 TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
163 OIDOID, -1, 0);
164 TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
165 INT2OID, -1, 0);
166 TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
167 INT8OID, -1, 0);
168 TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
169 BOOLOID, -1, 0);
170 TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
171 INT2OID, -1, 0);
172
174 TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
175 INT4OID, -1, 0);
176
177 fctx->tupdesc = BlessTupleDesc(tupledesc);
178
179 /* Allocate NBuffers worth of BufferCachePagesRec records. */
180 fctx->record = (BufferCachePagesRec *)
182 sizeof(BufferCachePagesRec) * NBuffers);
183
184 /* Set max calls and remember the user function context. */
185 funcctx->max_calls = NBuffers;
186 funcctx->user_fctx = fctx;
187
188 /* Return to original context when allocating transient memory */
189 MemoryContextSwitchTo(oldcontext);
190
191 /*
192 * Scan through all the buffers, saving the relevant fields in the
193 * fctx->record structure.
194 *
195 * We don't hold the partition locks, so we don't get a consistent
196 * snapshot across all buffers, but we do grab the buffer header
197 * locks, so the information of each buffer is self-consistent.
198 */
199 for (i = 0; i < NBuffers; i++)
200 {
203
205
207 /* Lock each buffer header before inspecting. */
209
210 fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
211 fctx->record[i].relfilenumber = BufTagGetRelNumber(&bufHdr->tag);
212 fctx->record[i].reltablespace = bufHdr->tag.spcOid;
213 fctx->record[i].reldatabase = bufHdr->tag.dbOid;
214 fctx->record[i].forknum = BufTagGetForkNum(&bufHdr->tag);
215 fctx->record[i].blocknum = bufHdr->tag.blockNum;
216 fctx->record[i].usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);
217 fctx->record[i].pinning_backends = BUF_STATE_GET_REFCOUNT(buf_state);
218
219 if (buf_state & BM_DIRTY)
220 fctx->record[i].isdirty = true;
221 else
222 fctx->record[i].isdirty = false;
223
224 /* Note if the buffer is valid, and has storage created */
226 fctx->record[i].isvalid = true;
227 else
228 fctx->record[i].isvalid = false;
229
231 }
232 }
233
235
236 /* Get the saved state */
237 fctx = funcctx->user_fctx;
238
239 if (funcctx->call_cntr < funcctx->max_calls)
240 {
241 uint32 i = funcctx->call_cntr;
243 bool nulls[NUM_BUFFERCACHE_PAGES_ELEM];
244
245 values[0] = Int32GetDatum(fctx->record[i].bufferid);
246 nulls[0] = false;
247
248 /*
249 * Set all fields except the bufferid to null if the buffer is unused
250 * or not valid.
251 */
252 if (fctx->record[i].blocknum == InvalidBlockNumber ||
253 fctx->record[i].isvalid == false)
254 {
255 nulls[1] = true;
256 nulls[2] = true;
257 nulls[3] = true;
258 nulls[4] = true;
259 nulls[5] = true;
260 nulls[6] = true;
261 nulls[7] = true;
262 /* unused for v1.0 callers, but the array is always long enough */
263 nulls[8] = true;
264 }
265 else
266 {
267 values[1] = ObjectIdGetDatum(fctx->record[i].relfilenumber);
268 nulls[1] = false;
269 values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
270 nulls[2] = false;
271 values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
272 nulls[3] = false;
273 values[4] = Int16GetDatum(fctx->record[i].forknum);
274 nulls[4] = false;
275 values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
276 nulls[5] = false;
277 values[6] = BoolGetDatum(fctx->record[i].isdirty);
278 nulls[6] = false;
279 values[7] = UInt16GetDatum(fctx->record[i].usagecount);
280 nulls[7] = false;
281 /* unused for v1.0 callers, but the array is always long enough */
282 values[8] = Int32GetDatum(fctx->record[i].pinning_backends);
283 nulls[8] = false;
284 }
285
286 /* Build and return the tuple. */
287 tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
288 result = HeapTupleGetDatum(tuple);
289
290 SRF_RETURN_NEXT(funcctx, result);
291 }
292 else
294}
295
296/*
297 * Inquire about OS pages mappings for shared buffers, with NUMA information,
298 * optionally.
299 *
300 * When "include_numa" is false, this routines ignores everything related
301 * to NUMA (returned as NULL values), returning mapping information between
302 * shared buffers and OS pages.
303 *
304 * When "include_numa" is true, NUMA is initialized and numa_node values
305 * are generated. In order to get reliable results we also need to touch
306 * memory pages, so that the inquiry about NUMA memory node does not return
307 * -2, indicating unmapped/unallocated pages.
308 *
309 * Buffers may be smaller or larger than OS memory pages. For each buffer we
310 * return one entry for each memory page used by the buffer (if the buffer is
311 * smaller, it only uses a part of one memory page).
312 *
313 * We expect both sizes (for buffers and memory pages) to be a power-of-2, so
314 * one is always a multiple of the other.
315 *
316 */
317static Datum
319{
321 MemoryContext oldcontext;
322 BufferCacheOsPagesContext *fctx; /* User function context. */
323 TupleDesc tupledesc;
325 HeapTuple tuple;
326 Datum result;
327
328 if (SRF_IS_FIRSTCALL())
329 {
330 int i,
331 idx;
334 int *os_page_status = NULL;
336 int max_entries;
337 char *startptr,
338 *endptr;
339
340 /* If NUMA information is requested, initialize NUMA support. */
341 if (include_numa && pg_numa_init() == -1)
342 elog(ERROR, "libnuma initialization failed or NUMA is not supported on this platform");
343
344 /*
345 * The database block size and OS memory page size are unlikely to be
346 * the same. The block size is 1-32KB, the memory page size depends on
347 * platform. On x86 it's usually 4KB, on ARM it's 4KB or 64KB, but
348 * there are also features like THP etc. Moreover, we don't quite know
349 * how the pages and buffers "align" in memory - the buffers may be
350 * shifted in some way, using more memory pages than necessary.
351 *
352 * So we need to be careful about mapping buffers to memory pages. We
353 * calculate the maximum number of pages a buffer might use, so that
354 * we allocate enough space for the entries. And then we count the
355 * actual number of entries as we scan the buffers.
356 *
357 * This information is needed before calling move_pages() for NUMA
358 * node id inquiry.
359 */
361
362 /*
363 * The pages and block size is expected to be 2^k, so one divides the
364 * other (we don't know in which direction). This does not say
365 * anything about relative alignment of pages/buffers.
366 */
367 Assert((os_page_size % BLCKSZ == 0) || (BLCKSZ % os_page_size == 0));
368
369 if (include_numa)
370 {
371 void **os_page_ptrs = NULL;
372
373 /*
374 * How many addresses we are going to query? Simply get the page
375 * for the first buffer, and first page after the last buffer, and
376 * count the pages from that.
377 */
378 startptr = (char *) TYPEALIGN_DOWN(os_page_size,
379 BufferGetBlock(1));
380 endptr = (char *) TYPEALIGN(os_page_size,
381 (char *) BufferGetBlock(NBuffers) + BLCKSZ);
382 os_page_count = (endptr - startptr) / os_page_size;
383
384 /* Used to determine the NUMA node for all OS pages at once */
387
388 /*
389 * Fill pointers for all the memory pages. This loop stores and
390 * touches (if needed) addresses into os_page_ptrs[] as input to
391 * one big move_pages(2) inquiry system call, as done in
392 * pg_numa_query_pages().
393 */
394 idx = 0;
395 for (char *ptr = startptr; ptr < endptr; ptr += os_page_size)
396 {
397 os_page_ptrs[idx++] = ptr;
398
399 /* Only need to touch memory once per backend process lifetime */
400 if (firstNumaTouch)
402 }
403
405
406 elog(DEBUG1, "NUMA: NBuffers=%d os_page_count=" UINT64_FORMAT " "
407 "os_page_size=%zu", NBuffers, os_page_count, os_page_size);
408
409 /*
410 * If we ever get 0xff back from kernel inquiry, then we probably
411 * have bug in our buffers to OS page mapping code here.
412 */
413 memset(os_page_status, 0xff, sizeof(int) * os_page_count);
414
415 /* Query NUMA status for all the pointers */
417 elog(ERROR, "failed NUMA pages inquiry: %m");
418 }
419
420 /* Initialize the multi-call context, load entries about buffers */
421
423
424 /* Switch context when allocating stuff to be used in later calls */
425 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
426
427 /* Create a user function context for cross-call persistence */
429
431 elog(ERROR, "return type must be a row type");
432
434 elog(ERROR, "incorrect number of output arguments");
435
436 /* Construct a tuple descriptor for the result rows. */
438 TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
439 INT4OID, -1, 0);
440 TupleDescInitEntry(tupledesc, (AttrNumber) 2, "os_page_num",
441 INT8OID, -1, 0);
442 TupleDescInitEntry(tupledesc, (AttrNumber) 3, "numa_node",
443 INT4OID, -1, 0);
444
445 fctx->tupdesc = BlessTupleDesc(tupledesc);
446 fctx->include_numa = include_numa;
447
448 /*
449 * Each buffer needs at least one entry, but it might be offset in
450 * some way, and use one extra entry. So we allocate space for the
451 * maximum number of entries we might need, and then count the exact
452 * number as we're walking buffers. That way we can do it in one pass,
453 * without reallocating memory.
454 */
457
458 /* Allocate entries for BufferCacheOsPagesRec records. */
459 fctx->record = (BufferCacheOsPagesRec *)
462
463 /* Return to original context when allocating transient memory */
464 MemoryContextSwitchTo(oldcontext);
465
466 if (include_numa && firstNumaTouch)
467 elog(DEBUG1, "NUMA: page-faulting the buffercache for proper NUMA readouts");
468
469 /*
470 * Scan through all the buffers, saving the relevant fields in the
471 * fctx->record structure.
472 *
473 * We don't hold the partition locks, so we don't get a consistent
474 * snapshot across all buffers, but we do grab the buffer header
475 * locks, so the information of each buffer is self-consistent.
476 */
477 startptr = (char *) TYPEALIGN_DOWN(os_page_size, (char *) BufferGetBlock(1));
478 idx = 0;
479 for (i = 0; i < NBuffers; i++)
480 {
481 char *buffptr = (char *) BufferGetBlock(i + 1);
483 uint32 bufferid;
484 int32 page_num;
485 char *startptr_buff,
487
489
491
492 /* Lock each buffer header before inspecting. */
496
497 /* start of the first page of this buffer */
499
500 /* end of the buffer (no need to align to memory page) */
502
504
505 /* calculate ID of the first page for this buffer */
506 page_num = (startptr_buff - startptr) / os_page_size;
507
508 /* Add an entry for each OS page overlapping with this buffer. */
509 for (char *ptr = startptr_buff; ptr < endptr_buff; ptr += os_page_size)
510 {
511 fctx->record[idx].bufferid = bufferid;
512 fctx->record[idx].page_num = page_num;
513 fctx->record[idx].numa_node = include_numa ? os_page_status[page_num] : -1;
514
515 /* advance to the next entry/page */
516 ++idx;
517 ++page_num;
518 }
519 }
520
522
523 if (include_numa)
525
526 /* Set max calls and remember the user function context. */
527 funcctx->max_calls = idx;
528 funcctx->user_fctx = fctx;
529
530 /* Remember this backend touched the pages (only relevant for NUMA) */
531 if (include_numa)
532 firstNumaTouch = false;
533 }
534
536
537 /* Get the saved state */
538 fctx = funcctx->user_fctx;
539
540 if (funcctx->call_cntr < funcctx->max_calls)
541 {
542 uint32 i = funcctx->call_cntr;
545
546 values[0] = Int32GetDatum(fctx->record[i].bufferid);
547 nulls[0] = false;
548
549 values[1] = Int64GetDatum(fctx->record[i].page_num);
550 nulls[1] = false;
551
552 if (fctx->include_numa)
553 {
554 /* status is valid node number */
555 if (fctx->record[i].numa_node >= 0)
556 {
557 values[2] = Int32GetDatum(fctx->record[i].numa_node);
558 nulls[2] = false;
559 }
560 else
561 {
562 /* some kind of error (e.g. pages moved to swap) */
563 values[2] = (Datum) 0;
564 nulls[2] = true;
565 }
566 }
567 else
568 {
569 values[2] = (Datum) 0;
570 nulls[2] = true;
571 }
572
573 /* Build and return the tuple. */
574 tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
575 result = HeapTupleGetDatum(tuple);
576
577 SRF_RETURN_NEXT(funcctx, result);
578 }
579 else
581}
582
583/*
584 * pg_buffercache_os_pages
585 *
586 * Retrieve information about OS pages, with or without NUMA information.
587 */
588Datum
590{
591 bool include_numa;
592
593 /* Get the boolean parameter that controls the NUMA behavior. */
594 include_numa = PG_GETARG_BOOL(0);
595
596 return pg_buffercache_os_pages_internal(fcinfo, include_numa);
597}
598
599/* Backward-compatible wrapper for v1.6. */
600Datum
602{
603 /* Call internal function with include_numa=true */
604 return pg_buffercache_os_pages_internal(fcinfo, true);
605}
606
607Datum
609{
610 Datum result;
611 TupleDesc tupledesc;
612 HeapTuple tuple;
615
621
622 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
623 elog(ERROR, "return type must be a row type");
624
625 for (int i = 0; i < NBuffers; i++)
626 {
629
631
632 /*
633 * This function summarizes the state of all headers. Locking the
634 * buffer headers wouldn't provide an improved result as the state of
635 * the buffer can still change after we release the lock and it'd
636 * noticeably increase the cost of the function.
637 */
640
641 if (buf_state & BM_VALID)
642 {
643 buffers_used++;
645
646 if (buf_state & BM_DIRTY)
648 }
649 else
651
654 }
655
656 memset(nulls, 0, sizeof(nulls));
661
662 if (buffers_used != 0)
664 else
665 nulls[4] = true;
666
667 /* Build and return the tuple. */
668 tuple = heap_form_tuple(tupledesc, values, nulls);
669 result = HeapTupleGetDatum(tuple);
670
671 PG_RETURN_DATUM(result);
672}
673
674Datum
676{
677 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
678 int usage_counts[BM_MAX_USAGE_COUNT + 1] = {0};
679 int dirty[BM_MAX_USAGE_COUNT + 1] = {0};
680 int pinned[BM_MAX_USAGE_COUNT + 1] = {0};
682 bool nulls[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM] = {0};
683
684 InitMaterializedSRF(fcinfo, 0);
685
686 for (int i = 0; i < NBuffers; i++)
687 {
690 int usage_count;
691
693
696
697 if (buf_state & BM_DIRTY)
698 dirty[usage_count]++;
699
701 pinned[usage_count]++;
702 }
703
704 for (int i = 0; i < BM_MAX_USAGE_COUNT + 1; i++)
705 {
706 values[0] = Int32GetDatum(i);
708 values[2] = Int32GetDatum(dirty[i]);
709 values[3] = Int32GetDatum(pinned[i]);
710
711 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
712 }
713
714 return (Datum) 0;
715}
716
717/*
718 * Helper function to check if the user has superuser privileges.
719 */
720static void
722{
723 if (!superuser())
726 errmsg("must be superuser to use %s()",
727 func_name)));
728}
729
730/*
731 * Try to evict a shared buffer.
732 */
733Datum
735{
736 Datum result;
737 TupleDesc tupledesc;
738 HeapTuple tuple;
740 bool nulls[NUM_BUFFERCACHE_EVICT_ELEM] = {0};
741
743 bool buffer_flushed;
744
745 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
746 elog(ERROR, "return type must be a row type");
747
748 pg_buffercache_superuser_check("pg_buffercache_evict");
749
751 elog(ERROR, "bad buffer ID: %d", buf);
752
755
756 tuple = heap_form_tuple(tupledesc, values, nulls);
757 result = HeapTupleGetDatum(tuple);
758
759 PG_RETURN_DATUM(result);
760}
761
762/*
763 * Try to evict specified relation.
764 */
765Datum
767{
768 Datum result;
769 TupleDesc tupledesc;
770 HeapTuple tuple;
772 bool nulls[NUM_BUFFERCACHE_EVICT_RELATION_ELEM] = {0};
773
774 Oid relOid;
775 Relation rel;
776
780
781 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
782 elog(ERROR, "return type must be a row type");
783
784 pg_buffercache_superuser_check("pg_buffercache_evict_relation");
785
786 relOid = PG_GETARG_OID(0);
787
788 rel = relation_open(relOid, AccessShareLock);
789
793 errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
794 "pg_buffercache_evict_relation")));
795
798
800
804
805 tuple = heap_form_tuple(tupledesc, values, nulls);
806 result = HeapTupleGetDatum(tuple);
807
808 PG_RETURN_DATUM(result);
809}
810
811
812/*
813 * Try to evict all shared buffers.
814 */
815Datum
817{
818 Datum result;
819 TupleDesc tupledesc;
820 HeapTuple tuple;
822 bool nulls[NUM_BUFFERCACHE_EVICT_ALL_ELEM] = {0};
823
827
828 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
829 elog(ERROR, "return type must be a row type");
830
831 pg_buffercache_superuser_check("pg_buffercache_evict_all");
832
835
839
840 tuple = heap_form_tuple(tupledesc, values, nulls);
841 result = HeapTupleGetDatum(tuple);
842
843 PG_RETURN_DATUM(result);
844}
845
846/*
847 * Try to mark a shared buffer as dirty.
848 */
849Datum
851{
852
853 Datum result;
854 TupleDesc tupledesc;
855 HeapTuple tuple;
857 bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_ELEM] = {0};
858
861
862 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
863 elog(ERROR, "return type must be a row type");
864
865 pg_buffercache_superuser_check("pg_buffercache_mark_dirty");
866
868 elog(ERROR, "bad buffer ID: %d", buf);
869
872
873 tuple = heap_form_tuple(tupledesc, values, nulls);
874 result = HeapTupleGetDatum(tuple);
875
876 PG_RETURN_DATUM(result);
877}
878
879/*
880 * Try to mark all the shared buffers of a relation as dirty.
881 */
882Datum
884{
885 Datum result;
886 TupleDesc tupledesc;
887 HeapTuple tuple;
890
891 Oid relOid;
892 Relation rel;
893
897
898 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
899 elog(ERROR, "return type must be a row type");
900
901 pg_buffercache_superuser_check("pg_buffercache_mark_dirty_relation");
902
903 relOid = PG_GETARG_OID(0);
904
905 rel = relation_open(relOid, AccessShareLock);
906
910 errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
911 "pg_buffercache_mark_dirty_relation")));
912
915
917
921
922 tuple = heap_form_tuple(tupledesc, values, nulls);
923 result = HeapTupleGetDatum(tuple);
924
925 PG_RETURN_DATUM(result);
926}
927
928/*
929 * Try to mark all the shared buffers as dirty.
930 */
931Datum
933{
934 Datum result;
935 TupleDesc tupledesc;
936 HeapTuple tuple;
938 bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM] = {0};
939
943
944 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
945 elog(ERROR, "return type must be a row type");
946
947 pg_buffercache_superuser_check("pg_buffercache_mark_dirty_all");
948
951
955
956 tuple = heap_form_tuple(tupledesc, values, nulls);
957 result = HeapTupleGetDatum(tuple);
958
959 PG_RETURN_DATUM(result);
960}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:262
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:467
int16 AttrNumber
Definition attnum.h:21
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
static Datum values[MAXATTR]
Definition bootstrap.c:155
int Buffer
Definition buf.h:23
#define BM_MAX_USAGE_COUNT
#define BM_TAG_VALID
static ForkNumber BufTagGetForkNum(const BufferTag *tag)
static RelFileNumber BufTagGetRelNumber(const BufferTag *tag)
static void UnlockBufHdr(BufferDesc *desc)
#define BM_DIRTY
#define BUF_STATE_GET_USAGECOUNT(state)
#define BUF_STATE_GET_REFCOUNT(state)
#define BM_VALID
static BufferDesc * GetBufferDescriptor(uint32 id)
static Buffer BufferDescriptorGetBuffer(const BufferDesc *bdesc)
void EvictAllUnpinnedBuffers(int32 *buffers_evicted, int32 *buffers_flushed, int32 *buffers_skipped)
Definition bufmgr.c:7571
void EvictRelUnpinnedBuffers(Relation rel, int32 *buffers_evicted, int32 *buffers_flushed, int32 *buffers_skipped)
Definition bufmgr.c:7621
uint64 LockBufHdr(BufferDesc *desc)
Definition bufmgr.c:7107
void MarkDirtyAllUnpinnedBuffers(int32 *buffers_dirtied, int32 *buffers_already_dirty, int32 *buffers_skipped)
Definition bufmgr.c:7821
bool MarkDirtyUnpinnedBuffer(Buffer buf, bool *buffer_already_dirty)
Definition bufmgr.c:7728
bool EvictUnpinnedBuffer(Buffer buf, bool *buffer_flushed)
Definition bufmgr.c:7542
void MarkDirtyRelUnpinnedBuffers(Relation rel, int32 *buffers_dirtied, int32 *buffers_already_dirty, int32 *buffers_skipped)
Definition bufmgr.c:7764
static Block BufferGetBlock(Buffer buffer)
Definition bufmgr.h:433
#define TYPEALIGN(ALIGNVAL, LEN)
Definition c.h:819
#define Max(x, y)
Definition c.h:991
#define Assert(condition)
Definition c.h:873
int64_t int64
Definition c.h:543
#define UINT64_FORMAT
Definition c.h:565
int32_t int32
Definition c.h:542
uint64_t uint64
Definition c.h:547
uint16_t uint16
Definition c.h:545
uint32_t uint32
Definition c.h:546
size_t Size
Definition c.h:619
#define TYPEALIGN_DOWN(ALIGNVAL, LEN)
Definition c.h:831
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define DEBUG1
Definition elog.h:30
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_array(type, count)
Definition fe_memutils.h:77
#define PG_GETARG_OID(n)
Definition fmgr.h:275
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_GETARG_INT32(n)
Definition fmgr.h:269
#define PG_GETARG_BOOL(n)
Definition fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition fmgr.h:354
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
Definition funcapi.c:76
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition funcapi.c:276
#define SRF_IS_FIRSTCALL()
Definition funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition funcapi.h:308
@ TYPEFUNC_COMPOSITE
Definition funcapi.h:149
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition funcapi.h:306
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition funcapi.h:328
int NBuffers
Definition globals.c:142
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1117
int i
Definition isn.c:77
#define AccessShareLock
Definition lockdefs.h:36
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void * MemoryContextAllocHuge(MemoryContext context, Size size)
Definition mcxt.c:1725
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
Datum pg_buffercache_os_pages(PG_FUNCTION_ARGS)
Datum pg_buffercache_evict_relation(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM
#define NUM_BUFFERCACHE_OS_PAGES_ELEM
Datum pg_buffercache_evict(PG_FUNCTION_ARGS)
Datum pg_buffercache_mark_dirty_relation(PG_FUNCTION_ARGS)
Datum pg_buffercache_summary(PG_FUNCTION_ARGS)
static void pg_buffercache_superuser_check(char *func_name)
Datum pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_SUMMARY_ELEM
Datum pg_buffercache_pages(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_EVICT_ELEM
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM
#define NUM_BUFFERCACHE_EVICT_ALL_ELEM
Datum pg_buffercache_evict_all(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM
#define NUM_BUFFERCACHE_PAGES_ELEM
#define NUM_BUFFERCACHE_MARK_DIRTY_ELEM
#define NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM
Datum pg_buffercache_mark_dirty_all(PG_FUNCTION_ARGS)
Datum pg_buffercache_mark_dirty(PG_FUNCTION_ARGS)
Datum pg_buffercache_numa_pages(PG_FUNCTION_ARGS)
static bool firstNumaTouch
static Datum pg_buffercache_os_pages_internal(FunctionCallInfo fcinfo, bool include_numa)
#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM
#define pg_numa_touch_mem_if_required(ptr)
Definition pg_numa.h:37
PGDLLIMPORT int pg_numa_query_pages(int pid, unsigned long count, void **pages, int *status)
Definition pg_numa.c:130
PGDLLIMPORT int pg_numa_init(void)
Definition pg_numa.c:123
static char buf[DEFAULT_XLOG_SEG_SIZE]
static Datum Int64GetDatum(int64 X)
Definition postgres.h:423
static Datum Int16GetDatum(int16 X)
Definition postgres.h:182
static Datum UInt16GetDatum(uint16 X)
Definition postgres.h:202
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:262
uint64_t Datum
Definition postgres.h:70
static Datum Float8GetDatum(float8 X)
Definition postgres.h:512
static Datum Int32GetDatum(int32 X)
Definition postgres.h:222
unsigned int Oid
static int fb(int x)
#define RelationUsesLocalBuffers(relation)
Definition rel.h:646
Oid RelFileNumber
Definition relpath.h:25
ForkNumber
Definition relpath.h:56
Size pg_get_shmem_pagesize(void)
Definition shmem.c:738
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:47
BufferCacheOsPagesRec * record
BufferCachePagesRec * record
bool superuser(void)
Definition superuser.c:46
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:182
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:842
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:784
const char * name