mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Cosmetic cleanup, not worth backpatching. Discussion: https://postgr.es/m/20200129200401.GA6303@alvherre.pgsql Reviewed-by: Tom Lane, Michael Paquier
		
			
				
	
	
		
			206 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*-------------------------------------------------------------------------
 | 
						|
 *
 | 
						|
 * pg_prewarm.c
 | 
						|
 *		  prewarming utilities
 | 
						|
 *
 | 
						|
 * Copyright (c) 2010-2020, PostgreSQL Global Development Group
 | 
						|
 *
 | 
						|
 * IDENTIFICATION
 | 
						|
 *		  contrib/pg_prewarm/pg_prewarm.c
 | 
						|
 *
 | 
						|
 *-------------------------------------------------------------------------
 | 
						|
 */
 | 
						|
#include "postgres.h"
 | 
						|
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "access/relation.h"
 | 
						|
#include "fmgr.h"
 | 
						|
#include "miscadmin.h"
 | 
						|
#include "storage/bufmgr.h"
 | 
						|
#include "storage/smgr.h"
 | 
						|
#include "utils/acl.h"
 | 
						|
#include "utils/builtins.h"
 | 
						|
#include "utils/lsyscache.h"
 | 
						|
#include "utils/rel.h"
 | 
						|
 | 
						|
PG_MODULE_MAGIC;
 | 
						|
 | 
						|
PG_FUNCTION_INFO_V1(pg_prewarm);
 | 
						|
 | 
						|
typedef enum
 | 
						|
{
 | 
						|
	PREWARM_PREFETCH,
 | 
						|
	PREWARM_READ,
 | 
						|
	PREWARM_BUFFER
 | 
						|
} PrewarmType;
 | 
						|
 | 
						|
static PGAlignedBlock blockbuffer;
 | 
						|
 | 
						|
/*
 | 
						|
 * pg_prewarm(regclass, mode text, fork text,
 | 
						|
 *			  first_block int8, last_block int8)
 | 
						|
 *
 | 
						|
 * The first argument is the relation to be prewarmed; the second controls
 | 
						|
 * how prewarming is done; legal options are 'prefetch', 'read', and 'buffer'.
 | 
						|
 * The third is the name of the relation fork to be prewarmed.  The fourth
 | 
						|
 * and fifth arguments specify the first and last block to be prewarmed.
 | 
						|
 * If the fourth argument is NULL, it will be taken as 0; if the fifth argument
 | 
						|
 * is NULL, it will be taken as the number of blocks in the relation.  The
 | 
						|
 * return value is the number of blocks successfully prewarmed.
 | 
						|
 */
 | 
						|
Datum
 | 
						|
pg_prewarm(PG_FUNCTION_ARGS)
 | 
						|
{
 | 
						|
	Oid			relOid;
 | 
						|
	text	   *forkName;
 | 
						|
	text	   *type;
 | 
						|
	int64		first_block;
 | 
						|
	int64		last_block;
 | 
						|
	int64		nblocks;
 | 
						|
	int64		blocks_done = 0;
 | 
						|
	int64		block;
 | 
						|
	Relation	rel;
 | 
						|
	ForkNumber	forkNumber;
 | 
						|
	char	   *forkString;
 | 
						|
	char	   *ttype;
 | 
						|
	PrewarmType ptype;
 | 
						|
	AclResult	aclresult;
 | 
						|
 | 
						|
	/* Basic sanity checking. */
 | 
						|
	if (PG_ARGISNULL(0))
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
				 errmsg("relation cannot be null")));
 | 
						|
	relOid = PG_GETARG_OID(0);
 | 
						|
	if (PG_ARGISNULL(1))
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
				 errmsg("prewarm type cannot be null")));
 | 
						|
	type = PG_GETARG_TEXT_PP(1);
 | 
						|
	ttype = text_to_cstring(type);
 | 
						|
	if (strcmp(ttype, "prefetch") == 0)
 | 
						|
		ptype = PREWARM_PREFETCH;
 | 
						|
	else if (strcmp(ttype, "read") == 0)
 | 
						|
		ptype = PREWARM_READ;
 | 
						|
	else if (strcmp(ttype, "buffer") == 0)
 | 
						|
		ptype = PREWARM_BUFFER;
 | 
						|
	else
 | 
						|
	{
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
				 errmsg("invalid prewarm type"),
 | 
						|
				 errhint("Valid prewarm types are \"prefetch\", \"read\", and \"buffer\".")));
 | 
						|
		PG_RETURN_INT64(0);		/* Placate compiler. */
 | 
						|
	}
 | 
						|
	if (PG_ARGISNULL(2))
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
				 errmsg("relation fork cannot be null")));
 | 
						|
	forkName = PG_GETARG_TEXT_PP(2);
 | 
						|
	forkString = text_to_cstring(forkName);
 | 
						|
	forkNumber = forkname_to_number(forkString);
 | 
						|
 | 
						|
	/* Open relation and check privileges. */
 | 
						|
	rel = relation_open(relOid, AccessShareLock);
 | 
						|
	aclresult = pg_class_aclcheck(relOid, GetUserId(), ACL_SELECT);
 | 
						|
	if (aclresult != ACLCHECK_OK)
 | 
						|
		aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), get_rel_name(relOid));
 | 
						|
 | 
						|
	/* Check that the fork exists. */
 | 
						|
	RelationOpenSmgr(rel);
 | 
						|
	if (!smgrexists(rel->rd_smgr, forkNumber))
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
				 errmsg("fork \"%s\" does not exist for this relation",
 | 
						|
						forkString)));
 | 
						|
 | 
						|
	/* Validate block numbers, or handle nulls. */
 | 
						|
	nblocks = RelationGetNumberOfBlocksInFork(rel, forkNumber);
 | 
						|
	if (PG_ARGISNULL(3))
 | 
						|
		first_block = 0;
 | 
						|
	else
 | 
						|
	{
 | 
						|
		first_block = PG_GETARG_INT64(3);
 | 
						|
		if (first_block < 0 || first_block >= nblocks)
 | 
						|
			ereport(ERROR,
 | 
						|
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
					 errmsg("starting block number must be between 0 and " INT64_FORMAT,
 | 
						|
							nblocks - 1)));
 | 
						|
	}
 | 
						|
	if (PG_ARGISNULL(4))
 | 
						|
		last_block = nblocks - 1;
 | 
						|
	else
 | 
						|
	{
 | 
						|
		last_block = PG_GETARG_INT64(4);
 | 
						|
		if (last_block < 0 || last_block >= nblocks)
 | 
						|
			ereport(ERROR,
 | 
						|
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 | 
						|
					 errmsg("ending block number must be between 0 and " INT64_FORMAT,
 | 
						|
							nblocks - 1)));
 | 
						|
	}
 | 
						|
 | 
						|
	/* Now we're ready to do the real work. */
 | 
						|
	if (ptype == PREWARM_PREFETCH)
 | 
						|
	{
 | 
						|
#ifdef USE_PREFETCH
 | 
						|
 | 
						|
		/*
 | 
						|
		 * In prefetch mode, we just hint the OS to read the blocks, but we
 | 
						|
		 * don't know whether it really does it, and we don't wait for it to
 | 
						|
		 * finish.
 | 
						|
		 *
 | 
						|
		 * It would probably be better to pass our prefetch requests in chunks
 | 
						|
		 * of a megabyte or maybe even a whole segment at a time, but there's
 | 
						|
		 * no practical way to do that at present without a gross modularity
 | 
						|
		 * violation, so we just do this.
 | 
						|
		 */
 | 
						|
		for (block = first_block; block <= last_block; ++block)
 | 
						|
		{
 | 
						|
			CHECK_FOR_INTERRUPTS();
 | 
						|
			PrefetchBuffer(rel, forkNumber, block);
 | 
						|
			++blocks_done;
 | 
						|
		}
 | 
						|
#else
 | 
						|
		ereport(ERROR,
 | 
						|
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 | 
						|
				 errmsg("prefetch is not supported by this build")));
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else if (ptype == PREWARM_READ)
 | 
						|
	{
 | 
						|
		/*
 | 
						|
		 * In read mode, we actually read the blocks, but not into shared
 | 
						|
		 * buffers.  This is more portable than prefetch mode (it works
 | 
						|
		 * everywhere) and is synchronous.
 | 
						|
		 */
 | 
						|
		for (block = first_block; block <= last_block; ++block)
 | 
						|
		{
 | 
						|
			CHECK_FOR_INTERRUPTS();
 | 
						|
			smgrread(rel->rd_smgr, forkNumber, block, blockbuffer.data);
 | 
						|
			++blocks_done;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (ptype == PREWARM_BUFFER)
 | 
						|
	{
 | 
						|
		/*
 | 
						|
		 * In buffer mode, we actually pull the data into shared_buffers.
 | 
						|
		 */
 | 
						|
		for (block = first_block; block <= last_block; ++block)
 | 
						|
		{
 | 
						|
			Buffer		buf;
 | 
						|
 | 
						|
			CHECK_FOR_INTERRUPTS();
 | 
						|
			buf = ReadBufferExtended(rel, forkNumber, block, RBM_NORMAL, NULL);
 | 
						|
			ReleaseBuffer(buf);
 | 
						|
			++blocks_done;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Close relation, release lock. */
 | 
						|
	relation_close(rel, AccessShareLock);
 | 
						|
 | 
						|
	PG_RETURN_INT64(blocks_done);
 | 
						|
}
 |