You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rawpage.c 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /*-------------------------------------------------------------------------
  2. *
  3. * rawpage.c
  4. * Functions to extract a raw page as bytea and inspect it
  5. *
  6. * Access-method specific inspection functions are in separate files.
  7. *
  8. * Copyright (c) 2007-2019, PostgreSQL Global Development Group
  9. *
  10. * IDENTIFICATION
  11. * contrib/pageinspect/rawpage.c
  12. *
  13. *-------------------------------------------------------------------------
  14. */
  15. #include "postgres.h"
  16. #include "pageinspect.h"
  17. #include "access/htup_details.h"
  18. #include "catalog/namespace.h"
  19. #include "catalog/pg_type.h"
  20. #include "funcapi.h"
  21. #include "miscadmin.h"
  22. #include "storage/bufmgr.h"
  23. #include "storage/checksum.h"
  24. #include "utils/builtins.h"
  25. #include "utils/pg_lsn.h"
  26. #include "utils/rel.h"
  27. #include "utils/varlena.h"
  28. PG_MODULE_MAGIC;
  29. static bytea *get_raw_page_internal(text *relname, ForkNumber forknum,
  30. BlockNumber blkno);
  31. /*
  32. * get_raw_page
  33. *
  34. * Returns a copy of a page from shared buffers as a bytea
  35. */
  36. PG_FUNCTION_INFO_V1(get_raw_page);
  37. Datum
  38. get_raw_page(PG_FUNCTION_ARGS)
  39. {
  40. text *relname = PG_GETARG_TEXT_PP(0);
  41. uint32 blkno = PG_GETARG_UINT32(1);
  42. bytea *raw_page;
  43. /*
  44. * We don't normally bother to check the number of arguments to a C
  45. * function, but here it's needed for safety because early 8.4 beta
  46. * releases mistakenly redefined get_raw_page() as taking three arguments.
  47. */
  48. if (PG_NARGS() != 2)
  49. ereport(ERROR,
  50. (errmsg("wrong number of arguments to get_raw_page()"),
  51. errhint("Run the updated pageinspect.sql script.")));
  52. raw_page = get_raw_page_internal(relname, MAIN_FORKNUM, blkno);
  53. PG_RETURN_BYTEA_P(raw_page);
  54. }
  55. /*
  56. * get_raw_page_fork
  57. *
  58. * Same, for any fork
  59. */
  60. PG_FUNCTION_INFO_V1(get_raw_page_fork);
  61. Datum
  62. get_raw_page_fork(PG_FUNCTION_ARGS)
  63. {
  64. text *relname = PG_GETARG_TEXT_PP(0);
  65. text *forkname = PG_GETARG_TEXT_PP(1);
  66. uint32 blkno = PG_GETARG_UINT32(2);
  67. bytea *raw_page;
  68. ForkNumber forknum;
  69. forknum = forkname_to_number(text_to_cstring(forkname));
  70. raw_page = get_raw_page_internal(relname, forknum, blkno);
  71. PG_RETURN_BYTEA_P(raw_page);
  72. }
  73. /*
  74. * workhorse
  75. */
  76. static bytea *
  77. get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
  78. {
  79. bytea *raw_page;
  80. RangeVar *relrv;
  81. Relation rel;
  82. char *raw_page_data;
  83. Buffer buf;
  84. if (!superuser())
  85. ereport(ERROR,
  86. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  87. (errmsg("must be superuser to use raw page functions"))));
  88. relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
  89. rel = relation_openrv(relrv, AccessShareLock);
  90. /* Check that this relation has storage */
  91. if (rel->rd_rel->relkind == RELKIND_VIEW)
  92. ereport(ERROR,
  93. (errcode(ERRCODE_WRONG_OBJECT_TYPE),
  94. errmsg("cannot get raw page from view \"%s\"",
  95. RelationGetRelationName(rel))));
  96. if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
  97. ereport(ERROR,
  98. (errcode(ERRCODE_WRONG_OBJECT_TYPE),
  99. errmsg("cannot get raw page from composite type \"%s\"",
  100. RelationGetRelationName(rel))));
  101. if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
  102. ereport(ERROR,
  103. (errcode(ERRCODE_WRONG_OBJECT_TYPE),
  104. errmsg("cannot get raw page from foreign table \"%s\"",
  105. RelationGetRelationName(rel))));
  106. if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
  107. ereport(ERROR,
  108. (errcode(ERRCODE_WRONG_OBJECT_TYPE),
  109. errmsg("cannot get raw page from partitioned table \"%s\"",
  110. RelationGetRelationName(rel))));
  111. if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
  112. ereport(ERROR,
  113. (errcode(ERRCODE_WRONG_OBJECT_TYPE),
  114. errmsg("cannot get raw page from partitioned index \"%s\"",
  115. RelationGetRelationName(rel))));
  116. /*
  117. * Reject attempts to read non-local temporary relations; we would be
  118. * likely to get wrong data since we have no visibility into the owning
  119. * session's local buffers.
  120. */
  121. if (RELATION_IS_OTHER_TEMP(rel))
  122. ereport(ERROR,
  123. (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  124. errmsg("cannot access temporary tables of other sessions")));
  125. if (blkno >= RelationGetNumberOfBlocksInFork(rel, forknum))
  126. ereport(ERROR,
  127. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  128. errmsg("block number %u is out of range for relation \"%s\"",
  129. blkno, RelationGetRelationName(rel))));
  130. /* Initialize buffer to copy to */
  131. raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ);
  132. SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ);
  133. raw_page_data = VARDATA(raw_page);
  134. /* Take a verbatim copy of the page */
  135. buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
  136. LockBuffer(buf, BUFFER_LOCK_SHARE);
  137. memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
  138. LockBuffer(buf, BUFFER_LOCK_UNLOCK);
  139. ReleaseBuffer(buf);
  140. relation_close(rel, AccessShareLock);
  141. return raw_page;
  142. }
  143. /*
  144. * get_page_from_raw
  145. *
  146. * Get a palloc'd, maxalign'ed page image from the result of get_raw_page()
  147. *
  148. * On machines with MAXALIGN = 8, the payload of a bytea is not maxaligned,
  149. * since it will start 4 bytes into a palloc'd value. On alignment-picky
  150. * machines, this will cause failures in accesses to 8-byte-wide values
  151. * within the page. We don't need to worry if accessing only 4-byte or
  152. * smaller fields, but when examining a struct that contains 8-byte fields,
  153. * use this function for safety.
  154. */
  155. Page
  156. get_page_from_raw(bytea *raw_page)
  157. {
  158. Page page;
  159. int raw_page_size;
  160. raw_page_size = VARSIZE_ANY_EXHDR(raw_page);
  161. if (raw_page_size != BLCKSZ)
  162. ereport(ERROR,
  163. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  164. errmsg("invalid page size"),
  165. errdetail("Expected %d bytes, got %d.",
  166. BLCKSZ, raw_page_size)));
  167. page = palloc(raw_page_size);
  168. memcpy(page, VARDATA_ANY(raw_page), raw_page_size);
  169. return page;
  170. }
  171. /*
  172. * page_header
  173. *
  174. * Allows inspection of page header fields of a raw page
  175. */
  176. PG_FUNCTION_INFO_V1(page_header);
  177. Datum
  178. page_header(PG_FUNCTION_ARGS)
  179. {
  180. bytea *raw_page = PG_GETARG_BYTEA_P(0);
  181. int raw_page_size;
  182. TupleDesc tupdesc;
  183. Datum result;
  184. HeapTuple tuple;
  185. Datum values[9];
  186. bool nulls[9];
  187. PageHeader page;
  188. XLogRecPtr lsn;
  189. if (!superuser())
  190. ereport(ERROR,
  191. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  192. (errmsg("must be superuser to use raw page functions"))));
  193. raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
  194. /*
  195. * Check that enough data was supplied, so that we don't try to access
  196. * fields outside the supplied buffer.
  197. */
  198. if (raw_page_size < SizeOfPageHeaderData)
  199. ereport(ERROR,
  200. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  201. errmsg("input page too small (%d bytes)", raw_page_size)));
  202. page = (PageHeader) VARDATA(raw_page);
  203. /* Build a tuple descriptor for our result type */
  204. if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  205. elog(ERROR, "return type must be a row type");
  206. /* Extract information from the page header */
  207. lsn = PageGetLSN(page);
  208. /* pageinspect >= 1.2 uses pg_lsn instead of text for the LSN field. */
  209. if (TupleDescAttr(tupdesc, 0)->atttypid == TEXTOID)
  210. {
  211. char lsnchar[64];
  212. snprintf(lsnchar, sizeof(lsnchar), "%X/%X",
  213. (uint32) (lsn >> 32), (uint32) lsn);
  214. values[0] = CStringGetTextDatum(lsnchar);
  215. }
  216. else
  217. values[0] = LSNGetDatum(lsn);
  218. values[1] = UInt16GetDatum(page->pd_checksum);
  219. values[2] = UInt16GetDatum(page->pd_flags);
  220. values[3] = UInt16GetDatum(page->pd_lower);
  221. values[4] = UInt16GetDatum(page->pd_upper);
  222. values[5] = UInt16GetDatum(page->pd_special);
  223. values[6] = UInt16GetDatum(PageGetPageSize(page));
  224. values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
  225. values[8] = TransactionIdGetDatum(page->pd_prune_xid);
  226. /* Build and return the tuple. */
  227. memset(nulls, 0, sizeof(nulls));
  228. tuple = heap_form_tuple(tupdesc, values, nulls);
  229. result = HeapTupleGetDatum(tuple);
  230. PG_RETURN_DATUM(result);
  231. }
  232. /*
  233. * page_checksum
  234. *
  235. * Compute checksum of a raw page
  236. */
  237. PG_FUNCTION_INFO_V1(page_checksum);
  238. Datum
  239. page_checksum(PG_FUNCTION_ARGS)
  240. {
  241. bytea *raw_page = PG_GETARG_BYTEA_P(0);
  242. uint32 blkno = PG_GETARG_INT32(1);
  243. int raw_page_size;
  244. PageHeader page;
  245. if (!superuser())
  246. ereport(ERROR,
  247. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  248. (errmsg("must be superuser to use raw page functions"))));
  249. raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
  250. /*
  251. * Check that the supplied page is of the right size.
  252. */
  253. if (raw_page_size != BLCKSZ)
  254. ereport(ERROR,
  255. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  256. errmsg("incorrect size of input page (%d bytes)", raw_page_size)));
  257. page = (PageHeader) VARDATA(raw_page);
  258. PG_RETURN_INT16(pg_checksum_page((char *) page, blkno));
  259. }