Age Owner Branch data TLA Line data Source code
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 "fmgr.h"
20 : : #include "miscadmin.h"
21 : : #include "storage/bufmgr.h"
22 : : #include "storage/read_stream.h"
23 : : #include "storage/smgr.h"
24 : : #include "utils/acl.h"
25 : : #include "utils/builtins.h"
26 : : #include "utils/lsyscache.h"
27 : : #include "utils/rel.h"
28 : :
164 tgl@sss.pgh.pa.us 29 :CBC 6 : PG_MODULE_MAGIC_EXT(
30 : : .name = "pg_prewarm",
31 : : .version = PG_VERSION
32 : : );
33 : :
4278 rhaas@postgresql.org 34 : 9 : PG_FUNCTION_INFO_V1(pg_prewarm);
35 : :
36 : : typedef enum
37 : : {
38 : : PREWARM_PREFETCH,
39 : : PREWARM_READ,
40 : : PREWARM_BUFFER,
41 : : } PrewarmType;
42 : :
43 : : static PGIOAlignedBlock blockbuffer;
44 : :
45 : : /*
46 : : * pg_prewarm(regclass, mode text, fork text,
47 : : * first_block int8, last_block int8)
48 : : *
49 : : * The first argument is the relation to be prewarmed; the second controls
50 : : * how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'.
51 : : * The third is the name of the relation fork to be prewarmed. The fourth
52 : : * and fifth arguments specify the first and last block to be prewarmed.
53 : : * If the fourth argument is NULL, it will be taken as 0; if the fifth argument
54 : : * is NULL, it will be taken as the number of blocks in the relation. The
55 : : * return value is the number of blocks successfully prewarmed.
56 : : */
57 : : Datum
58 : 2734 : pg_prewarm(PG_FUNCTION_ARGS)
59 : : {
60 : : Oid relOid;
61 : : text *forkName;
62 : : text *type;
63 : : int64 first_block;
64 : : int64 last_block;
65 : : int64 nblocks;
66 : 2734 : int64 blocks_done = 0;
67 : : int64 block;
68 : : Relation rel;
69 : : ForkNumber forkNumber;
70 : : char *forkString;
71 : : char *ttype;
72 : : PrewarmType ptype;
73 : : AclResult aclresult;
74 : :
75 : : /* Basic sanity checking. */
76 [ - + ]: 2734 : if (PG_ARGISNULL(0))
4278 rhaas@postgresql.org 77 [ # # ]:UBC 0 : ereport(ERROR,
78 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79 : : errmsg("relation cannot be null")));
4278 rhaas@postgresql.org 80 :CBC 2734 : relOid = PG_GETARG_OID(0);
81 [ - + ]: 2734 : if (PG_ARGISNULL(1))
4278 rhaas@postgresql.org 82 [ # # ]:UBC 0 : ereport(ERROR,
83 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
84 : : errmsg("prewarm type cannot be null")));
3100 noah@leadboat.com 85 :CBC 2734 : type = PG_GETARG_TEXT_PP(1);
4278 rhaas@postgresql.org 86 : 2734 : ttype = text_to_cstring(type);
87 [ + + ]: 2734 : if (strcmp(ttype, "prefetch") == 0)
88 : 1 : ptype = PREWARM_PREFETCH;
89 [ + + ]: 2733 : else if (strcmp(ttype, "read") == 0)
90 : 1 : ptype = PREWARM_READ;
91 [ + - ]: 2732 : else if (strcmp(ttype, "buffer") == 0)
92 : 2732 : ptype = PREWARM_BUFFER;
93 : : else
94 : : {
4278 rhaas@postgresql.org 95 [ # # ]:UBC 0 : ereport(ERROR,
96 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
97 : : errmsg("invalid prewarm type"),
98 : : errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\".")));
99 : : PG_RETURN_INT64(0); /* Placate compiler. */
100 : : }
4278 rhaas@postgresql.org 101 [ - + ]:CBC 2734 : if (PG_ARGISNULL(2))
4278 rhaas@postgresql.org 102 [ # # ]:UBC 0 : ereport(ERROR,
103 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
104 : : errmsg("relation fork cannot be null")));
3100 noah@leadboat.com 105 :CBC 2734 : forkName = PG_GETARG_TEXT_PP(2);
4278 rhaas@postgresql.org 106 : 2734 : forkString = text_to_cstring(forkName);
107 : 2734 : forkNumber = forkname_to_number(forkString);
108 : :
109 : : /* Open relation and check privileges. */
110 : 2734 : rel = relation_open(relOid, AccessShareLock);
111 : 2734 : aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT);
112 [ - + ]: 2734 : if (aclresult != ACLCHECK_OK)
2835 peter_e@gmx.net 113 :UBC 0 : aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), get_rel_name(relOid));
114 : :
115 : : /* Check that the relation has storage. */
100 fujii@postgresql.org 116 [ + + + + :CBC 2734 : if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+ - + + +
- ]
117 [ + - ]: 1 : ereport(ERROR,
118 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
119 : : errmsg("relation \"%s\" does not have storage",
120 : : RelationGetRelationName(rel)),
121 : : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
122 : :
123 : : /* Check that the fork exists. */
1517 tgl@sss.pgh.pa.us 124 [ - + ]: 2733 : if (!smgrexists(RelationGetSmgr(rel), forkNumber))
4278 rhaas@postgresql.org 125 [ # # ]:UBC 0 : ereport(ERROR,
126 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
127 : : errmsg("fork \"%s\" does not exist for this relation",
128 : : forkString)));
129 : :
130 : : /* Validate block numbers, or handle nulls. */
4278 rhaas@postgresql.org 131 :CBC 2733 : nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber);
132 [ + - ]: 2733 : if (PG_ARGISNULL(3))
133 : 2733 : first_block = 0;
134 : : else
135 : : {
4278 rhaas@postgresql.org 136 :UBC 0 : first_block = PG_GETARG_INT64(3);
137 [ # # # # ]: 0 : if (first_block < 0 || first_block >= nblocks)
138 [ # # ]: 0 : ereport(ERROR,
139 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
140 : : errmsg("starting block number must be between 0 and %" PRId64,
141 : : (nblocks - 1))));
142 : : }
4278 rhaas@postgresql.org 143 [ + - ]:CBC 2733 : if (PG_ARGISNULL(4))
144 : 2733 : last_block = nblocks - 1;
145 : : else
146 : : {
4278 rhaas@postgresql.org 147 :UBC 0 : last_block = PG_GETARG_INT64(4);
148 [ # # # # ]: 0 : if (last_block < 0 || last_block >= nblocks)
149 [ # # ]: 0 : ereport(ERROR,
150 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
151 : : errmsg("ending block number must be between 0 and %" PRId64,
152 : : (nblocks - 1))));
153 : : }
154 : :
155 : : /* Now we're ready to do the real work. */
4278 rhaas@postgresql.org 156 [ + + ]:CBC 2733 : if (ptype == PREWARM_PREFETCH)
157 : : {
158 : : #ifdef USE_PREFETCH
159 : :
160 : : /*
161 : : * In prefetch mode, we just hint the OS to read the blocks, but we
162 : : * don't know whether it really does it, and we don't wait for it to
163 : : * finish.
164 : : *
165 : : * It would probably be better to pass our prefetch requests in chunks
166 : : * of a megabyte or maybe even a whole segment at a time, but there's
167 : : * no practical way to do that at present without a gross modularity
168 : : * violation, so we just do this.
169 : : */
170 [ + + ]: 2 : for (block = first_block; block <= last_block; ++block)
171 : : {
3951 andres@anarazel.de 172 [ - + ]: 1 : CHECK_FOR_INTERRUPTS();
4278 rhaas@postgresql.org 173 : 1 : PrefetchBuffer(rel, forkNumber, block);
174 : 1 : ++blocks_done;
175 : : }
176 : : #else
177 : : ereport(ERROR,
178 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
179 : : errmsg("prefetch is not supported by this build")));
180 : : #endif
181 : : }
182 [ + + ]: 2732 : else if (ptype == PREWARM_READ)
183 : : {
184 : : /*
185 : : * In read mode, we actually read the blocks, but not into shared
186 : : * buffers. This is more portable than prefetch mode (it works
187 : : * everywhere) and is synchronous.
188 : : */
189 [ + + ]: 2 : for (block = first_block; block <= last_block; ++block)
190 : : {
3951 andres@anarazel.de 191 [ - + ]: 1 : CHECK_FOR_INTERRUPTS();
1517 tgl@sss.pgh.pa.us 192 : 1 : smgrread(RelationGetSmgr(rel), forkNumber, block, blockbuffer.data);
4278 rhaas@postgresql.org 193 : 1 : ++blocks_done;
194 : : }
195 : : }
196 [ + - ]: 2731 : else if (ptype == PREWARM_BUFFER)
197 : : {
198 : : BlockRangeReadStreamPrivate p;
199 : : ReadStream *stream;
200 : :
201 : : /*
202 : : * In buffer mode, we actually pull the data into shared_buffers.
203 : : */
204 : :
205 : : /* Set up the private state for our streaming buffer read callback. */
368 noah@leadboat.com 206 : 2731 : p.current_blocknum = first_block;
207 : 2731 : p.last_exclusive = last_block + 1;
208 : :
209 : : /*
210 : : * It is safe to use batchmode as block_range_read_stream_cb takes no
211 : : * locks.
212 : : */
131 melanieplageman@gmai 213 : 2731 : stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
214 : : READ_STREAM_FULL |
215 : : READ_STREAM_USE_BATCHING,
216 : : NULL,
217 : : rel,
218 : : forkNumber,
219 : : block_range_read_stream_cb,
220 : : &p,
221 : : 0);
222 : :
4278 rhaas@postgresql.org 223 [ + + ]: 12272 : for (block = first_block; block <= last_block; ++block)
224 : : {
225 : : Buffer buf;
226 : :
3951 andres@anarazel.de 227 [ - + ]: 9541 : CHECK_FOR_INTERRUPTS();
521 tmunro@postgresql.or 228 : 9541 : buf = read_stream_next_buffer(stream, NULL);
4278 rhaas@postgresql.org 229 : 9541 : ReleaseBuffer(buf);
230 : 9541 : ++blocks_done;
231 : : }
521 tmunro@postgresql.or 232 [ - + ]: 2731 : Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
233 : 2731 : read_stream_end(stream);
234 : : }
235 : :
236 : : /* Close relation, release lock. */
4278 rhaas@postgresql.org 237 : 2733 : relation_close(rel, AccessShareLock);
238 : :
239 : 2733 : PG_RETURN_INT64(blocks_done);
240 : : }
|