*
* Access-method specific inspection functions are in separate files.
*
- * Copyright (c) 2007, PostgreSQL Global Development Group
+ * Copyright (c) 2007-2013, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/contrib/pageinspect/rawpage.c,v 1.2 2007/09/21 21:25:42 tgl Exp $
+ * contrib/pageinspect/rawpage.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include "fmgr.h"
-#include "funcapi.h"
-#include "access/heapam.h"
-#include "access/transam.h"
+#include "access/htup_details.h"
+#include "catalog/catalog.h"
#include "catalog/namespace.h"
-#include "catalog/pg_type.h"
-#include "utils/builtins.h"
+#include "funcapi.h"
#include "miscadmin.h"
+#include "storage/bufmgr.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
PG_MODULE_MAGIC;
-Datum get_raw_page(PG_FUNCTION_ARGS);
-Datum page_header(PG_FUNCTION_ARGS);
+Datum get_raw_page(PG_FUNCTION_ARGS);
+Datum get_raw_page_fork(PG_FUNCTION_ARGS);
+Datum page_header(PG_FUNCTION_ARGS);
+
+static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
+ BlockNumber blkno);
+
/*
* get_raw_page
{
text *relname = PG_GETARG_TEXT_P(0);
uint32 blkno = PG_GETARG_UINT32(1);
+ bytea *raw_page;
+
+ /*
+ * We don't normally bother to check the number of arguments to a C
+ * function, but here it's needed for safety because early 8.4 beta
+ * releases mistakenly redefined get_raw_page() as taking three arguments.
+ */
+ if (PG_NARGS() != 2)
+ ereport(ERROR,
+ (errmsg("wrong number of arguments to get_raw_page()"),
+ errhint("Run the updated pageinspect.sql script.")));
+
+ raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
+
+ PG_RETURN_BYTEA_P(raw_page);
+}
+
+/*
+ * get_raw_page_fork
+ *
+ * Same, for any fork
+ */
+PG_FUNCTION_INFO_V1(get_raw_page_fork);
+
+Datum
+get_raw_page_fork(PG_FUNCTION_ARGS)
+{
+ text *relname = PG_GETARG_TEXT_P(0);
+ text *forkname = PG_GETARG_TEXT_P(1);
+ uint32 blkno = PG_GETARG_UINT32(2);
+ bytea *raw_page;
+ ForkNumber forknum;
+
+ forknum = forkname_to_number(text_to_cstring(forkname));
+
+ raw_page = get_raw_page_internal(relname, forknum, blkno);
+ PG_RETURN_BYTEA_P(raw_page);
+}
+
+/*
+ * workhorse
+ */
+static bytea *
+get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
+{
+ bytea *raw_page;
+ RangeVar *relrv;
Relation rel;
- RangeVar *relrv;
- bytea *raw_page;
- char *raw_page_data;
+ char *raw_page_data;
Buffer buf;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot get raw page from view \"%s\"",
- RelationGetRelationName(rel))));
+ RelationGetRelationName(rel))));
if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot get raw page from composite type \"%s\"",
- RelationGetRelationName(rel))));
+ RelationGetRelationName(rel))));
+ if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot get raw page from foreign table \"%s\"",
+ RelationGetRelationName(rel))));
+
+ /*
+ * Reject attempts to read non-local temporary relations; we would be
+ * likely to get wrong data since we have no visibility into the owning
+ * session's local buffers.
+ */
+ if (RELATION_IS_OTHER_TEMP(rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot access temporary tables of other sessions")));
if (blkno >= RelationGetNumberOfBlocks(rel))
elog(ERROR, "block number %u is out of range for relation \"%s\"",
/* Take a verbatim copy of the page */
- buf = ReadBuffer(rel, blkno);
+ buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_SHARE);
memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
relation_close(rel, AccessShareLock);
- PG_RETURN_BYTEA_P(raw_page);
+ return raw_page;
}
/*
raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
/*
- * Check that enough data was supplied, so that we don't try to access
- * fields outside the supplied buffer.
+ * Check that enough data was supplied, so that we don't try to access
+ * fields outside the supplied buffer.
*/
- if(raw_page_size < sizeof(PageHeaderData))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("input page too small (%d bytes)", raw_page_size)));
+ if (raw_page_size < sizeof(PageHeaderData))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("input page too small (%d bytes)", raw_page_size)));
page = (PageHeader) VARDATA(raw_page);
/* Extract information from the page header */
lsn = PageGetLSN(page);
- snprintf(lsnchar, sizeof(lsnchar), "%X/%X", lsn.xlogid, lsn.xrecoff);
+ snprintf(lsnchar, sizeof(lsnchar), "%X/%X",
+ (uint32) (lsn >> 32), (uint32) lsn);
- values[0] = DirectFunctionCall1(textin, CStringGetDatum(lsnchar));
+ values[0] = CStringGetTextDatum(lsnchar);
values[1] = UInt16GetDatum(PageGetTLI(page));
values[2] = UInt16GetDatum(page->pd_flags);
values[3] = UInt16GetDatum(page->pd_lower);
values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
values[8] = TransactionIdGetDatum(page->pd_prune_xid);
- /* Build and return the tuple. */
+ /* Build and return the tuple. */
memset(nulls, 0, sizeof(nulls));
- tuple = heap_form_tuple(tupdesc, values, nulls);
- result = HeapTupleGetDatum(tuple);
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ result = HeapTupleGetDatum(tuple);
PG_RETURN_DATUM(result);
}