PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
pg_prewarm.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_prewarm.c
4 * prewarming utilities
5 *
6 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
7 *
8 * IDENTIFICATION
9 * contrib/pg_prewarm/pg_prewarm.c
10 *
11 *-------------------------------------------------------------------------
12 */
13#include "postgres.h"
14
15#include <sys/stat.h>
16#include <unistd.h>
17
18#include "access/relation.h"
19#include "catalog/index.h"
20#include "fmgr.h"
21#include "miscadmin.h"
22#include "storage/bufmgr.h"
23#include "storage/lmgr.h"
24#include "storage/read_stream.h"
25#include "storage/smgr.h"
26#include "utils/acl.h"
27#include "utils/builtins.h"
28#include "utils/lsyscache.h"
29#include "utils/rel.h"
30
32 .name = "pg_prewarm",
33 .version = PG_VERSION
34);
35
37
38typedef enum
39{
44
46
47/*
48 * pg_prewarm(regclass, mode text, fork text,
49 * first_block int8, last_block int8)
50 *
51 * The first argument is the relation to be prewarmed; the second controls
52 * how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'.
53 * The third is the name of the relation fork to be prewarmed. The fourth
54 * and fifth arguments specify the first and last block to be prewarmed.
55 * If the fourth argument is NULL, it will be taken as 0; if the fifth argument
56 * is NULL, it will be taken as the number of blocks in the relation. The
57 * return value is the number of blocks successfully prewarmed.
58 */
61{
62 Oid relOid;
63 text *forkName;
64 text *type;
65 int64 first_block;
66 int64 last_block;
67 int64 nblocks;
68 int64 blocks_done = 0;
69 int64 block;
70 Relation rel;
71 ForkNumber forkNumber;
72 char *forkString;
73 char *ttype;
74 PrewarmType ptype;
75 AclResult aclresult;
76 char relkind;
77 Oid privOid;
78
79 /* Basic sanity checking. */
80 if (PG_ARGISNULL(0))
82 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
83 errmsg("relation cannot be null")));
84 relOid = PG_GETARG_OID(0);
85 if (PG_ARGISNULL(1))
87 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
88 errmsg("prewarm type cannot be null")));
90 ttype = text_to_cstring(type);
91 if (strcmp(ttype, "prefetch") == 0)
92 ptype = PREWARM_PREFETCH;
93 else if (strcmp(ttype, "read") == 0)
94 ptype = PREWARM_READ;
95 else if (strcmp(ttype, "buffer") == 0)
96 ptype = PREWARM_BUFFER;
97 else
98 {
100 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
101 errmsg("invalid prewarm type"),
102 errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\".")));
103 PG_RETURN_INT64(0); /* Placate compiler. */
104 }
105 if (PG_ARGISNULL(2))
107 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
108 errmsg("relation fork cannot be null")));
109 forkName = PG_GETARG_TEXT_PP(2);
110 forkString = text_to_cstring(forkName);
111 forkNumber = forkname_to_number(forkString);
112
113 /*
114 * Open relation and check privileges. If the relation is an index, we
115 * must check the privileges on its parent table instead.
116 */
117 relkind = get_rel_relkind(relOid);
118 if (relkind == RELKIND_INDEX ||
119 relkind == RELKIND_PARTITIONED_INDEX)
120 {
121 privOid = IndexGetRelation(relOid, true);
122
123 /* Lock table before index to avoid deadlock. */
124 if (OidIsValid(privOid))
126 }
127 else
128 privOid = relOid;
129
130 rel = relation_open(relOid, AccessShareLock);
131
132 /*
133 * It's possible that the relation with OID "privOid" was dropped and the
134 * OID was reused before we locked it. If that happens, we could be left
135 * with the wrong parent table OID, in which case we must ERROR. It's
136 * possible that such a race would change the outcome of
137 * get_rel_relkind(), too, but the worst case scenario there is that we'll
138 * check privileges on the index instead of its parent table, which isn't
139 * too terrible.
140 */
141 if (!OidIsValid(privOid) ||
142 (privOid != relOid &&
143 privOid != IndexGetRelation(relOid, true)))
146 errmsg("could not find parent table of index \"%s\"",
148
149 aclresult = pg_class_aclcheck(privOid, GetUserId(), ACL_SELECT);
150 if (aclresult != ACLCHECK_OK)
151 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), get_rel_name(relOid));
152
153 /* Check that the relation has storage. */
154 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
156 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
157 errmsg("relation \"%s\" does not have storage",
160
161 /* Check that the fork exists. */
162 if (!smgrexists(RelationGetSmgr(rel), forkNumber))
164 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
165 errmsg("fork \"%s\" does not exist for this relation",
166 forkString)));
167
168 /* Validate block numbers, or handle nulls. */
169 nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber);
170 if (PG_ARGISNULL(3))
171 first_block = 0;
172 else
173 {
174 first_block = PG_GETARG_INT64(3);
175 if (first_block < 0 || first_block >= nblocks)
177 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
178 errmsg("starting block number must be between 0 and %" PRId64,
179 (nblocks - 1))));
180 }
181 if (PG_ARGISNULL(4))
182 last_block = nblocks - 1;
183 else
184 {
185 last_block = PG_GETARG_INT64(4);
186 if (last_block < 0 || last_block >= nblocks)
188 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
189 errmsg("ending block number must be between 0 and %" PRId64,
190 (nblocks - 1))));
191 }
192
193 /* Now we're ready to do the real work. */
194 if (ptype == PREWARM_PREFETCH)
195 {
196#ifdef USE_PREFETCH
197
198 /*
199 * In prefetch mode, we just hint the OS to read the blocks, but we
200 * don't know whether it really does it, and we don't wait for it to
201 * finish.
202 *
203 * It would probably be better to pass our prefetch requests in chunks
204 * of a megabyte or maybe even a whole segment at a time, but there's
205 * no practical way to do that at present without a gross modularity
206 * violation, so we just do this.
207 */
208 for (block = first_block; block <= last_block; ++block)
209 {
211 PrefetchBuffer(rel, forkNumber, block);
212 ++blocks_done;
213 }
214#else
216 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
217 errmsg("prefetch is not supported by this build")));
218#endif
219 }
220 else if (ptype == PREWARM_READ)
221 {
222 /*
223 * In read mode, we actually read the blocks, but not into shared
224 * buffers. This is more portable than prefetch mode (it works
225 * everywhere) and is synchronous.
226 */
227 for (block = first_block; block <= last_block; ++block)
228 {
230 smgrread(RelationGetSmgr(rel), forkNumber, block, blockbuffer.data);
231 ++blocks_done;
232 }
233 }
234 else if (ptype == PREWARM_BUFFER)
235 {
237 ReadStream *stream;
238
239 /*
240 * In buffer mode, we actually pull the data into shared_buffers.
241 */
242
243 /* Set up the private state for our streaming buffer read callback. */
244 p.current_blocknum = first_block;
245 p.last_exclusive = last_block + 1;
246
247 /*
248 * It is safe to use batchmode as block_range_read_stream_cb takes no
249 * locks.
250 */
254 NULL,
255 rel,
256 forkNumber,
258 &p,
259 0);
260
261 for (block = first_block; block <= last_block; ++block)
262 {
263 Buffer buf;
264
266 buf = read_stream_next_buffer(stream, NULL);
268 ++blocks_done;
269 }
271 read_stream_end(stream);
272 }
273
274 /* Close relation, release locks. */
276
277 if (privOid != relOid)
279
280 PG_RETURN_INT64(blocks_done);
281}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2652
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4037
int Buffer
Definition: buf.h:23
#define InvalidBuffer
Definition: buf.h:25
PrefetchBufferResult PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
Definition: bufmgr.c:653
BlockNumber RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
Definition: bufmgr.c:4437
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5371
int64_t int64
Definition: c.h:539
#define OidIsValid(objectId)
Definition: c.h:778
int errhint(const char *fmt,...)
Definition: elog.c:1330
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_ARGISNULL(n)
Definition: fmgr.h:209
#define PG_RETURN_INT64(x)
Definition: fmgr.h:368
#define PG_GETARG_INT64(n)
Definition: fmgr.h:283
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
Assert(PointerIsAligned(start, uint64))
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
#define AccessShareLock
Definition: lockdefs.h:36
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2095
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2170
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
Oid GetUserId(void)
Definition: miscinit.c:469
ObjectType get_relkind_objtype(char relkind)
#define ACL_SELECT
Definition: parsenodes.h:77
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
PG_FUNCTION_INFO_V1(pg_prewarm)
PrewarmType
Definition: pg_prewarm.c:39
@ PREWARM_PREFETCH
Definition: pg_prewarm.c:40
@ PREWARM_READ
Definition: pg_prewarm.c:41
@ PREWARM_BUFFER
Definition: pg_prewarm.c:42
PG_MODULE_MAGIC_EXT(.name="pg_prewarm",.version=PG_VERSION)
static PGIOAlignedBlock blockbuffer
Definition: pg_prewarm.c:45
Datum pg_prewarm(PG_FUNCTION_ARGS)
Definition: pg_prewarm.c:60
static char * buf
Definition: pg_test_fsync.c:72
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:79
uint64_t Datum
Definition: postgres.h:70
unsigned int Oid
Definition: postgres_ext.h:32
Buffer read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
Definition: read_stream.c:791
ReadStream * read_stream_begin_relation(int flags, BufferAccessStrategy strategy, Relation rel, ForkNumber forknum, ReadStreamBlockNumberCB callback, void *callback_private_data, size_t per_buffer_data_size)
Definition: read_stream.c:737
void read_stream_end(ReadStream *stream)
Definition: read_stream.c:1089
BlockNumber block_range_read_stream_cb(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
Definition: read_stream.c:162
#define READ_STREAM_MAINTENANCE
Definition: read_stream.h:28
#define READ_STREAM_USE_BATCHING
Definition: read_stream.h:64
#define READ_STREAM_FULL
Definition: read_stream.h:43
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:577
#define RelationGetRelationName(relation)
Definition: rel.h:549
ForkNumber forkname_to_number(const char *forkName)
Definition: relpath.c:50
ForkNumber
Definition: relpath.h:56
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
static void smgrread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, void *buffer)
Definition: smgr.h:124
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
Form_pg_class rd_rel
Definition: rel.h:111
Definition: c.h:696
char data[BLCKSZ]
Definition: c.h:1140
char * text_to_cstring(const text *t)
Definition: varlena.c:214
const char * type
const char * name