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