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