1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00
Files
postgres/src/backend/utils/adt/pagefuncs.c
Michael Paquier 60a51c6b32 Fix incorrect placement of pfree() in pg_relation_check_pages()
This would cause the function to crash when more than one page is
considered as broken and reported in the SRF.

Reported-by: Noriyoshi Shinoda
Discussion: https://postgr.es/m/TU4PR8401MB11523D42C315AAF822E74275EE170@TU4PR8401MB1152.NAMPRD84.PROD.OUTLOOK.COM
2020-10-29 09:17:34 +09:00

230 lines
6.0 KiB
C

/*-------------------------------------------------------------------------
*
* pagefuncs.c
* Functions for features related to relation pages.
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/adt/pagefuncs.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/relation.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
static void check_one_relation(TupleDesc tupdesc, Tuplestorestate *tupstore,
Oid relid, ForkNumber single_forknum);
static void check_relation_fork(TupleDesc tupdesc, Tuplestorestate *tupstore,
Relation relation, ForkNumber forknum);
/*
* callback arguments for check_pages_error_callback()
*/
typedef struct CheckPagesErrorInfo
{
char *path;
BlockNumber blkno;
} CheckPagesErrorInfo;
/*
* Error callback specific to check_relation_fork().
*/
static void
check_pages_error_callback(void *arg)
{
CheckPagesErrorInfo *errinfo = (CheckPagesErrorInfo *) arg;
errcontext("while checking page %u of path %s",
errinfo->blkno, errinfo->path);
}
/*
* pg_relation_check_pages
*
* Check the state of all the pages for one or more fork types in the given
* relation.
*/
Datum
pg_relation_check_pages(PG_FUNCTION_ARGS)
{
Oid relid;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
TupleDesc tupdesc;
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
ForkNumber forknum;
/* Switch into long-lived context to construct returned data structures */
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore;
rsinfo->setDesc = tupdesc;
MemoryContextSwitchTo(oldcontext);
/* handle arguments */
if (PG_ARGISNULL(0))
{
/* Just leave if nothing is defined */
PG_RETURN_VOID();
}
/* By default all the forks of a relation are checked */
if (PG_ARGISNULL(1))
forknum = InvalidForkNumber;
else
{
const char *forkname = TextDatumGetCString(PG_GETARG_TEXT_PP(1));
forknum = forkname_to_number(forkname);
}
relid = PG_GETARG_OID(0);
check_one_relation(tupdesc, tupstore, relid, forknum);
tuplestore_donestoring(tupstore);
return (Datum) 0;
}
/*
* Perform the check on a single relation, possibly filtered with a single
* fork. This function will check if the given relation exists or not, as
* a relation could be dropped after checking for the list of relations and
* before getting here, and we don't want to error out in this case.
*/
static void
check_one_relation(TupleDesc tupdesc, Tuplestorestate *tupstore,
Oid relid, ForkNumber single_forknum)
{
Relation relation;
ForkNumber forknum;
/* Check if relation exists. leaving if there is no such relation */
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
return;
relation = relation_open(relid, AccessShareLock);
/*
* Sanity checks, returning no results if not supported. Temporary
* relations and relations without storage are out of scope.
*/
if (!RELKIND_HAS_STORAGE(relation->rd_rel->relkind) ||
relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
{
relation_close(relation, AccessShareLock);
return;
}
RelationOpenSmgr(relation);
for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
{
if (single_forknum != InvalidForkNumber && single_forknum != forknum)
continue;
if (smgrexists(relation->rd_smgr, forknum))
check_relation_fork(tupdesc, tupstore, relation, forknum);
}
relation_close(relation, AccessShareLock);
}
/*
* For a given relation and fork, do the real work of iterating over all pages
* and doing the check. Caller must hold an AccessShareLock lock on the given
* relation.
*/
static void
check_relation_fork(TupleDesc tupdesc, Tuplestorestate *tupstore,
Relation relation, ForkNumber forknum)
{
BlockNumber blkno,
nblocks;
SMgrRelation smgr = relation->rd_smgr;
char *path;
CheckPagesErrorInfo errinfo;
ErrorContextCallback errcallback;
/* Number of output arguments in the SRF */
#define PG_CHECK_RELATION_COLS 2
Assert(CheckRelationLockedByMe(relation, AccessShareLock, true));
/*
* We remember the number of blocks here. Since caller must hold a lock
* on the relation, we know that it won't be truncated while we are
* iterating over the blocks. Any block added after this function started
* will not be checked.
*/
nblocks = RelationGetNumberOfBlocksInFork(relation, forknum);
path = relpathbackend(smgr->smgr_rnode.node,
smgr->smgr_rnode.backend,
forknum);
/*
* Error context to print some information about blocks and relations
* impacted by corruptions.
*/
errinfo.path = pstrdup(path);
errinfo.blkno = 0;
errcallback.callback = check_pages_error_callback;
errcallback.arg = (void *) &errinfo;
errcallback.previous = error_context_stack;
error_context_stack = &errcallback;
for (blkno = 0; blkno < nblocks; blkno++)
{
Datum values[PG_CHECK_RELATION_COLS];
bool nulls[PG_CHECK_RELATION_COLS];
int i = 0;
/* Update block number for the error context */
errinfo.blkno = blkno;
CHECK_FOR_INTERRUPTS();
/* Check the given buffer */
if (CheckBuffer(smgr, forknum, blkno))
continue;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
values[i++] = CStringGetTextDatum(path);
values[i++] = Int64GetDatum((int64) blkno);
Assert(i == PG_CHECK_RELATION_COLS);
/* Save the corrupted blocks in the tuplestore. */
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
pfree(path);
/* Pop the error context stack */
error_context_stack = errcallback.previous;
}