1 /*-------------------------------------------------------------------------
6 * Copyright (c) 2010-2021, PostgreSQL Global Development Group
9 * contrib/pg_prewarm/pg_prewarm.c
11 *-------------------------------------------------------------------------
18 #include "access/relation.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"
30 PG_FUNCTION_INFO_V1(pg_prewarm
);
39 static PGAlignedBlock blockbuffer
;
42 * pg_prewarm(regclass, mode text, fork text,
43 * first_block int8, last_block int8)
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.
54 pg_prewarm(PG_FUNCTION_ARGS
)
62 int64 blocks_done
= 0;
65 ForkNumber forkNumber
;
71 /* Basic sanity checking. */
74 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
75 errmsg("relation cannot be null")));
76 relOid
= PG_GETARG_OID(0);
79 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
80 errmsg("prewarm type cannot be null")));
81 type
= PG_GETARG_TEXT_PP(1);
82 ttype
= text_to_cstring(type
);
83 if (strcmp(ttype
, "prefetch") == 0)
84 ptype
= PREWARM_PREFETCH
;
85 else if (strcmp(ttype
, "read") == 0)
87 else if (strcmp(ttype
, "buffer") == 0)
88 ptype
= PREWARM_BUFFER
;
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. */
99 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
100 errmsg("relation fork cannot be null")));
101 forkName
= PG_GETARG_TEXT_PP(2);
102 forkString
= text_to_cstring(forkName
);
103 forkNumber
= forkname_to_number(forkString
);
105 /* Open relation and check privileges. */
106 rel
= relation_open(relOid
, AccessShareLock
);
107 aclresult
= pg_class_aclcheck(relOid
, GetUserId(), ACL_SELECT
);
108 if (aclresult
!= ACLCHECK_OK
)
109 aclcheck_error(aclresult
, get_relkind_objtype(rel
->rd_rel
->relkind
), get_rel_name(relOid
));
111 /* Check that the fork exists. */
112 if (!smgrexists(RelationGetSmgr(rel
), forkNumber
))
114 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
115 errmsg("fork \"%s\" does not exist for this relation",
118 /* Validate block numbers, or handle nulls. */
119 nblocks
= RelationGetNumberOfBlocksInFork(rel
, forkNumber
);
124 first_block
= PG_GETARG_INT64(3);
125 if (first_block
< 0 || first_block
>= nblocks
)
127 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
128 errmsg("starting block number must be between 0 and %lld",
129 (long long) (nblocks
- 1))));
132 last_block
= nblocks
- 1;
135 last_block
= PG_GETARG_INT64(4);
136 if (last_block
< 0 || last_block
>= nblocks
)
138 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
139 errmsg("ending block number must be between 0 and %lld",
140 (long long) (nblocks
- 1))));
143 /* Now we're ready to do the real work. */
144 if (ptype
== PREWARM_PREFETCH
)
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
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.
158 for (block
= first_block
; block
<= last_block
; ++block
)
160 CHECK_FOR_INTERRUPTS();
161 PrefetchBuffer(rel
, forkNumber
, block
);
166 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
167 errmsg("prefetch is not supported by this build")));
170 else if (ptype
== PREWARM_READ
)
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.
177 for (block
= first_block
; block
<= last_block
; ++block
)
179 CHECK_FOR_INTERRUPTS();
180 smgrread(RelationGetSmgr(rel
), forkNumber
, block
, blockbuffer
.data
);
184 else if (ptype
== PREWARM_BUFFER
)
187 * In buffer mode, we actually pull the data into shared_buffers.
189 for (block
= first_block
; block
<= last_block
; ++block
)
193 CHECK_FOR_INTERRUPTS();
194 buf
= ReadBufferExtended(rel
, forkNumber
, block
, RBM_NORMAL
, NULL
);
200 /* Close relation, release lock. */
201 relation_close(rel
, AccessShareLock
);
203 PG_RETURN_INT64(blocks_done
);