Header And Logo

PostgreSQL
| The world's most advanced open source database.

rawpage.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * rawpage.c
00004  *    Functions to extract a raw page as bytea and inspect it
00005  *
00006  * Access-method specific inspection functions are in separate files.
00007  *
00008  * Copyright (c) 2007-2013, PostgreSQL Global Development Group
00009  *
00010  * IDENTIFICATION
00011  *    contrib/pageinspect/rawpage.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 
00016 #include "postgres.h"
00017 
00018 #include "access/htup_details.h"
00019 #include "catalog/catalog.h"
00020 #include "catalog/namespace.h"
00021 #include "funcapi.h"
00022 #include "miscadmin.h"
00023 #include "storage/bufmgr.h"
00024 #include "utils/builtins.h"
00025 #include "utils/rel.h"
00026 
00027 PG_MODULE_MAGIC;
00028 
00029 Datum       get_raw_page(PG_FUNCTION_ARGS);
00030 Datum       get_raw_page_fork(PG_FUNCTION_ARGS);
00031 Datum       page_header(PG_FUNCTION_ARGS);
00032 
00033 static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
00034                       BlockNumber blkno);
00035 
00036 
00037 /*
00038  * get_raw_page
00039  *
00040  * Returns a copy of a page from shared buffers as a bytea
00041  */
00042 PG_FUNCTION_INFO_V1(get_raw_page);
00043 
00044 Datum
00045 get_raw_page(PG_FUNCTION_ARGS)
00046 {
00047     text       *relname = PG_GETARG_TEXT_P(0);
00048     uint32      blkno = PG_GETARG_UINT32(1);
00049     bytea      *raw_page;
00050 
00051     /*
00052      * We don't normally bother to check the number of arguments to a C
00053      * function, but here it's needed for safety because early 8.4 beta
00054      * releases mistakenly redefined get_raw_page() as taking three arguments.
00055      */
00056     if (PG_NARGS() != 2)
00057         ereport(ERROR,
00058                 (errmsg("wrong number of arguments to get_raw_page()"),
00059                  errhint("Run the updated pageinspect.sql script.")));
00060 
00061     raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
00062 
00063     PG_RETURN_BYTEA_P(raw_page);
00064 }
00065 
00066 /*
00067  * get_raw_page_fork
00068  *
00069  * Same, for any fork
00070  */
00071 PG_FUNCTION_INFO_V1(get_raw_page_fork);
00072 
00073 Datum
00074 get_raw_page_fork(PG_FUNCTION_ARGS)
00075 {
00076     text       *relname = PG_GETARG_TEXT_P(0);
00077     text       *forkname = PG_GETARG_TEXT_P(1);
00078     uint32      blkno = PG_GETARG_UINT32(2);
00079     bytea      *raw_page;
00080     ForkNumber  forknum;
00081 
00082     forknum = forkname_to_number(text_to_cstring(forkname));
00083 
00084     raw_page = get_raw_page_internal(relname, forknum, blkno);
00085 
00086     PG_RETURN_BYTEA_P(raw_page);
00087 }
00088 
00089 /*
00090  * workhorse
00091  */
00092 static bytea *
00093 get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
00094 {
00095     bytea      *raw_page;
00096     RangeVar   *relrv;
00097     Relation    rel;
00098     char       *raw_page_data;
00099     Buffer      buf;
00100 
00101     if (!superuser())
00102         ereport(ERROR,
00103                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00104                  (errmsg("must be superuser to use raw functions"))));
00105 
00106     relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
00107     rel = relation_openrv(relrv, AccessShareLock);
00108 
00109     /* Check that this relation has storage */
00110     if (rel->rd_rel->relkind == RELKIND_VIEW)
00111         ereport(ERROR,
00112                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00113                  errmsg("cannot get raw page from view \"%s\"",
00114                         RelationGetRelationName(rel))));
00115     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
00116         ereport(ERROR,
00117                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00118                  errmsg("cannot get raw page from composite type \"%s\"",
00119                         RelationGetRelationName(rel))));
00120     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
00121         ereport(ERROR,
00122                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00123                  errmsg("cannot get raw page from foreign table \"%s\"",
00124                         RelationGetRelationName(rel))));
00125 
00126     /*
00127      * Reject attempts to read non-local temporary relations; we would be
00128      * likely to get wrong data since we have no visibility into the owning
00129      * session's local buffers.
00130      */
00131     if (RELATION_IS_OTHER_TEMP(rel))
00132         ereport(ERROR,
00133                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00134                  errmsg("cannot access temporary tables of other sessions")));
00135 
00136     if (blkno >= RelationGetNumberOfBlocks(rel))
00137         elog(ERROR, "block number %u is out of range for relation \"%s\"",
00138              blkno, RelationGetRelationName(rel));
00139 
00140     /* Initialize buffer to copy to */
00141     raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
00142     SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
00143     raw_page_data = VARDATA(raw_page);
00144 
00145     /* Take a verbatim copy of the page */
00146 
00147     buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
00148     LockBuffer(buf, BUFFER_LOCK_SHARE);
00149 
00150     memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
00151 
00152     LockBuffer(buf, BUFFER_LOCK_UNLOCK);
00153     ReleaseBuffer(buf);
00154 
00155     relation_close(rel, AccessShareLock);
00156 
00157     return raw_page;
00158 }
00159 
00160 /*
00161  * page_header
00162  *
00163  * Allows inspection of page header fields of a raw page
00164  */
00165 
00166 PG_FUNCTION_INFO_V1(page_header);
00167 
00168 Datum
00169 page_header(PG_FUNCTION_ARGS)
00170 {
00171     bytea      *raw_page = PG_GETARG_BYTEA_P(0);
00172     int         raw_page_size;
00173 
00174     TupleDesc   tupdesc;
00175 
00176     Datum       result;
00177     HeapTuple   tuple;
00178     Datum       values[9];
00179     bool        nulls[9];
00180 
00181     PageHeader  page;
00182     XLogRecPtr  lsn;
00183     char        lsnchar[64];
00184 
00185     if (!superuser())
00186         ereport(ERROR,
00187                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00188                  (errmsg("must be superuser to use raw page functions"))));
00189 
00190     raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
00191 
00192     /*
00193      * Check that enough data was supplied, so that we don't try to access
00194      * fields outside the supplied buffer.
00195      */
00196     if (raw_page_size < sizeof(PageHeaderData))
00197         ereport(ERROR,
00198                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00199                  errmsg("input page too small (%d bytes)", raw_page_size)));
00200 
00201     page = (PageHeader) VARDATA(raw_page);
00202 
00203     /* Build a tuple descriptor for our result type */
00204     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
00205         elog(ERROR, "return type must be a row type");
00206 
00207     /* Extract information from the page header */
00208 
00209     lsn = PageGetLSN(page);
00210     snprintf(lsnchar, sizeof(lsnchar), "%X/%X",
00211              (uint32) (lsn >> 32), (uint32) lsn);
00212 
00213     values[0] = CStringGetTextDatum(lsnchar);
00214     values[1] = UInt16GetDatum(page->pd_checksum);
00215     values[2] = UInt16GetDatum(page->pd_flags);
00216     values[3] = UInt16GetDatum(page->pd_lower);
00217     values[4] = UInt16GetDatum(page->pd_upper);
00218     values[5] = UInt16GetDatum(page->pd_special);
00219     values[6] = UInt16GetDatum(PageGetPageSize(page));
00220     values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
00221     values[8] = TransactionIdGetDatum(page->pd_prune_xid);
00222 
00223     /* Build and return the tuple. */
00224 
00225     memset(nulls, 0, sizeof(nulls));
00226 
00227     tuple = heap_form_tuple(tupdesc, values, nulls);
00228     result = HeapTupleGetDatum(tuple);
00229 
00230     PG_RETURN_DATUM(result);
00231 }