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.

heapfuncs.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /*-------------------------------------------------------------------------
  2. *
  3. * heapfuncs.c
  4. * Functions to investigate heap pages
  5. *
  6. * We check the input to these functions for corrupt pointers etc. that
  7. * might cause crashes, but at the same time we try to print out as much
  8. * information as possible, even if it's nonsense. That's because if a
  9. * page is corrupt, we don't know why and how exactly it is corrupt, so we
  10. * let the user judge it.
  11. *
  12. * These functions are restricted to superusers for the fear of introducing
  13. * security holes if the input checking isn't as water-tight as it should be.
  14. * You'd need to be superuser to obtain a raw page image anyway, so
  15. * there's hardly any use case for using these without superuser-rights
  16. * anyway.
  17. *
  18. * Copyright (c) 2007-2019, PostgreSQL Global Development Group
  19. *
  20. * IDENTIFICATION
  21. * contrib/pageinspect/heapfuncs.c
  22. *
  23. *-------------------------------------------------------------------------
  24. */
  25. #include "postgres.h"
  26. #include "pageinspect.h"
  27. #include "access/htup_details.h"
  28. #include "funcapi.h"
  29. #include "catalog/pg_type.h"
  30. #include "miscadmin.h"
  31. #include "utils/array.h"
  32. #include "utils/builtins.h"
  33. #include "utils/rel.h"
  34. /*
  35. * It's not supported to create tuples with oids anymore, but when pg_upgrade
  36. * was used to upgrade from an older version, tuples might still have an
  37. * oid. Seems worthwhile to display that.
  38. */
  39. #define HeapTupleHeaderGetOidOld(tup) \
  40. ( \
  41. ((tup)->t_infomask & HEAP_HASOID_OLD) ? \
  42. *((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) \
  43. : \
  44. InvalidOid \
  45. )
  46. /*
  47. * bits_to_text
  48. *
  49. * Converts a bits8-array of 'len' bits to a human-readable
  50. * c-string representation.
  51. */
  52. static char *
  53. bits_to_text(bits8 *bits, int len)
  54. {
  55. int i;
  56. char *str;
  57. str = palloc(len + 1);
  58. for (i = 0; i < len; i++)
  59. str[i] = (bits[(i / 8)] & (1 << (i % 8))) ? '1' : '0';
  60. str[i] = '\0';
  61. return str;
  62. }
  63. /*
  64. * text_to_bits
  65. *
  66. * Converts a c-string representation of bits into a bits8-array. This is
  67. * the reverse operation of previous routine.
  68. */
  69. static bits8 *
  70. text_to_bits(char *str, int len)
  71. {
  72. bits8 *bits;
  73. int off = 0;
  74. char byte = 0;
  75. bits = palloc(len + 1);
  76. while (off < len)
  77. {
  78. if (off % 8 == 0)
  79. byte = 0;
  80. if ((str[off] == '0') || (str[off] == '1'))
  81. byte = byte | ((str[off] - '0') << off % 8);
  82. else
  83. ereport(ERROR,
  84. (errcode(ERRCODE_DATA_CORRUPTED),
  85. errmsg("illegal character '%c' in t_bits string", str[off])));
  86. if (off % 8 == 7)
  87. bits[off / 8] = byte;
  88. off++;
  89. }
  90. return bits;
  91. }
  92. /*
  93. * heap_page_items
  94. *
  95. * Allows inspection of line pointers and tuple headers of a heap page.
  96. */
  97. PG_FUNCTION_INFO_V1(heap_page_items);
  98. typedef struct heap_page_items_state
  99. {
  100. TupleDesc tupd;
  101. Page page;
  102. uint16 offset;
  103. } heap_page_items_state;
  104. Datum
  105. heap_page_items(PG_FUNCTION_ARGS)
  106. {
  107. bytea *raw_page = PG_GETARG_BYTEA_P(0);
  108. heap_page_items_state *inter_call_data = NULL;
  109. FuncCallContext *fctx;
  110. int raw_page_size;
  111. if (!superuser())
  112. ereport(ERROR,
  113. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  114. (errmsg("must be superuser to use raw page functions"))));
  115. raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
  116. if (SRF_IS_FIRSTCALL())
  117. {
  118. TupleDesc tupdesc;
  119. MemoryContext mctx;
  120. if (raw_page_size < SizeOfPageHeaderData)
  121. ereport(ERROR,
  122. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  123. errmsg("input page too small (%d bytes)", raw_page_size)));
  124. fctx = SRF_FIRSTCALL_INIT();
  125. mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
  126. inter_call_data = palloc(sizeof(heap_page_items_state));
  127. /* Build a tuple descriptor for our result type */
  128. if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
  129. elog(ERROR, "return type must be a row type");
  130. inter_call_data->tupd = tupdesc;
  131. inter_call_data->offset = FirstOffsetNumber;
  132. inter_call_data->page = VARDATA(raw_page);
  133. fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page);
  134. fctx->user_fctx = inter_call_data;
  135. MemoryContextSwitchTo(mctx);
  136. }
  137. fctx = SRF_PERCALL_SETUP();
  138. inter_call_data = fctx->user_fctx;
  139. if (fctx->call_cntr < fctx->max_calls)
  140. {
  141. Page page = inter_call_data->page;
  142. HeapTuple resultTuple;
  143. Datum result;
  144. ItemId id;
  145. Datum values[14];
  146. bool nulls[14];
  147. uint16 lp_offset;
  148. uint16 lp_flags;
  149. uint16 lp_len;
  150. memset(nulls, 0, sizeof(nulls));
  151. /* Extract information from the line pointer */
  152. id = PageGetItemId(page, inter_call_data->offset);
  153. lp_offset = ItemIdGetOffset(id);
  154. lp_flags = ItemIdGetFlags(id);
  155. lp_len = ItemIdGetLength(id);
  156. values[0] = UInt16GetDatum(inter_call_data->offset);
  157. values[1] = UInt16GetDatum(lp_offset);
  158. values[2] = UInt16GetDatum(lp_flags);
  159. values[3] = UInt16GetDatum(lp_len);
  160. /*
  161. * We do just enough validity checking to make sure we don't reference
  162. * data outside the page passed to us. The page could be corrupt in
  163. * many other ways, but at least we won't crash.
  164. */
  165. if (ItemIdHasStorage(id) &&
  166. lp_len >= MinHeapTupleSize &&
  167. lp_offset == MAXALIGN(lp_offset) &&
  168. lp_offset + lp_len <= raw_page_size)
  169. {
  170. HeapTupleHeader tuphdr;
  171. bytea *tuple_data_bytea;
  172. int tuple_data_len;
  173. /* Extract information from the tuple header */
  174. tuphdr = (HeapTupleHeader) PageGetItem(page, id);
  175. values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
  176. values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr));
  177. /* shared with xvac */
  178. values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr));
  179. values[7] = PointerGetDatum(&tuphdr->t_ctid);
  180. values[8] = UInt32GetDatum(tuphdr->t_infomask2);
  181. values[9] = UInt32GetDatum(tuphdr->t_infomask);
  182. values[10] = UInt8GetDatum(tuphdr->t_hoff);
  183. /* Copy raw tuple data into bytea attribute */
  184. tuple_data_len = lp_len - tuphdr->t_hoff;
  185. tuple_data_bytea = (bytea *) palloc(tuple_data_len + VARHDRSZ);
  186. SET_VARSIZE(tuple_data_bytea, tuple_data_len + VARHDRSZ);
  187. memcpy(VARDATA(tuple_data_bytea), (char *) tuphdr + tuphdr->t_hoff,
  188. tuple_data_len);
  189. values[13] = PointerGetDatum(tuple_data_bytea);
  190. /*
  191. * We already checked that the item is completely within the raw
  192. * page passed to us, with the length given in the line pointer.
  193. * Let's check that t_hoff doesn't point over lp_len, before using
  194. * it to access t_bits and oid.
  195. */
  196. if (tuphdr->t_hoff >= SizeofHeapTupleHeader &&
  197. tuphdr->t_hoff <= lp_len &&
  198. tuphdr->t_hoff == MAXALIGN(tuphdr->t_hoff))
  199. {
  200. if (tuphdr->t_infomask & HEAP_HASNULL)
  201. {
  202. int bits_len;
  203. bits_len =
  204. BITMAPLEN(HeapTupleHeaderGetNatts(tuphdr)) * BITS_PER_BYTE;
  205. values[11] = CStringGetTextDatum(
  206. bits_to_text(tuphdr->t_bits, bits_len));
  207. }
  208. else
  209. nulls[11] = true;
  210. if (tuphdr->t_infomask & HEAP_HASOID_OLD)
  211. values[12] = HeapTupleHeaderGetOidOld(tuphdr);
  212. else
  213. nulls[12] = true;
  214. }
  215. else
  216. {
  217. nulls[11] = true;
  218. nulls[12] = true;
  219. }
  220. }
  221. else
  222. {
  223. /*
  224. * The line pointer is not used, or it's invalid. Set the rest of
  225. * the fields to NULL
  226. */
  227. int i;
  228. for (i = 4; i <= 13; i++)
  229. nulls[i] = true;
  230. }
  231. /* Build and return the result tuple. */
  232. resultTuple = heap_form_tuple(inter_call_data->tupd, values, nulls);
  233. result = HeapTupleGetDatum(resultTuple);
  234. inter_call_data->offset++;
  235. SRF_RETURN_NEXT(fctx, result);
  236. }
  237. else
  238. SRF_RETURN_DONE(fctx);
  239. }
  240. /*
  241. * tuple_data_split_internal
  242. *
  243. * Split raw tuple data taken directly from a page into an array of bytea
  244. * elements. This routine does a lookup on NULL values and creates array
  245. * elements accordingly. This is a reimplementation of nocachegetattr()
  246. * in heaptuple.c simplified for educational purposes.
  247. */
  248. static Datum
  249. tuple_data_split_internal(Oid relid, char *tupdata,
  250. uint16 tupdata_len, uint16 t_infomask,
  251. uint16 t_infomask2, bits8 *t_bits,
  252. bool do_detoast)
  253. {
  254. ArrayBuildState *raw_attrs;
  255. int nattrs;
  256. int i;
  257. int off = 0;
  258. Relation rel;
  259. TupleDesc tupdesc;
  260. /* Get tuple descriptor from relation OID */
  261. rel = relation_open(relid, AccessShareLock);
  262. tupdesc = RelationGetDescr(rel);
  263. raw_attrs = initArrayResult(BYTEAOID, CurrentMemoryContext, false);
  264. nattrs = tupdesc->natts;
  265. if (nattrs < (t_infomask2 & HEAP_NATTS_MASK))
  266. ereport(ERROR,
  267. (errcode(ERRCODE_DATA_CORRUPTED),
  268. errmsg("number of attributes in tuple header is greater than number of attributes in tuple descriptor")));
  269. for (i = 0; i < nattrs; i++)
  270. {
  271. Form_pg_attribute attr;
  272. bool is_null;
  273. bytea *attr_data = NULL;
  274. attr = TupleDescAttr(tupdesc, i);
  275. /*
  276. * Tuple header can specify less attributes than tuple descriptor as
  277. * ALTER TABLE ADD COLUMN without DEFAULT keyword does not actually
  278. * change tuples in pages, so attributes with numbers greater than
  279. * (t_infomask2 & HEAP_NATTS_MASK) should be treated as NULL.
  280. */
  281. if (i >= (t_infomask2 & HEAP_NATTS_MASK))
  282. is_null = true;
  283. else
  284. is_null = (t_infomask & HEAP_HASNULL) && att_isnull(i, t_bits);
  285. if (!is_null)
  286. {
  287. int len;
  288. if (attr->attlen == -1)
  289. {
  290. off = att_align_pointer(off, attr->attalign, -1,
  291. tupdata + off);
  292. /*
  293. * As VARSIZE_ANY throws an exception if it can't properly
  294. * detect the type of external storage in macros VARTAG_SIZE,
  295. * this check is repeated to have a nicer error handling.
  296. */
  297. if (VARATT_IS_EXTERNAL(tupdata + off) &&
  298. !VARATT_IS_EXTERNAL_ONDISK(tupdata + off) &&
  299. !VARATT_IS_EXTERNAL_INDIRECT(tupdata + off))
  300. ereport(ERROR,
  301. (errcode(ERRCODE_DATA_CORRUPTED),
  302. errmsg("first byte of varlena attribute is incorrect for attribute %d", i)));
  303. len = VARSIZE_ANY(tupdata + off);
  304. }
  305. else
  306. {
  307. off = att_align_nominal(off, attr->attalign);
  308. len = attr->attlen;
  309. }
  310. if (tupdata_len < off + len)
  311. ereport(ERROR,
  312. (errcode(ERRCODE_DATA_CORRUPTED),
  313. errmsg("unexpected end of tuple data")));
  314. if (attr->attlen == -1 && do_detoast)
  315. attr_data = DatumGetByteaPCopy(tupdata + off);
  316. else
  317. {
  318. attr_data = (bytea *) palloc(len + VARHDRSZ);
  319. SET_VARSIZE(attr_data, len + VARHDRSZ);
  320. memcpy(VARDATA(attr_data), tupdata + off, len);
  321. }
  322. off = att_addlength_pointer(off, attr->attlen,
  323. tupdata + off);
  324. }
  325. raw_attrs = accumArrayResult(raw_attrs, PointerGetDatum(attr_data),
  326. is_null, BYTEAOID, CurrentMemoryContext);
  327. if (attr_data)
  328. pfree(attr_data);
  329. }
  330. if (tupdata_len != off)
  331. ereport(ERROR,
  332. (errcode(ERRCODE_DATA_CORRUPTED),
  333. errmsg("end of tuple reached without looking at all its data")));
  334. relation_close(rel, AccessShareLock);
  335. return makeArrayResult(raw_attrs, CurrentMemoryContext);
  336. }
  337. /*
  338. * tuple_data_split
  339. *
  340. * Split raw tuple data taken directly from page into distinct elements
  341. * taking into account null values.
  342. */
  343. PG_FUNCTION_INFO_V1(tuple_data_split);
  344. Datum
  345. tuple_data_split(PG_FUNCTION_ARGS)
  346. {
  347. Oid relid;
  348. bytea *raw_data;
  349. uint16 t_infomask;
  350. uint16 t_infomask2;
  351. char *t_bits_str;
  352. bool do_detoast = false;
  353. bits8 *t_bits = NULL;
  354. Datum res;
  355. relid = PG_GETARG_OID(0);
  356. raw_data = PG_ARGISNULL(1) ? NULL : PG_GETARG_BYTEA_P(1);
  357. t_infomask = PG_GETARG_INT16(2);
  358. t_infomask2 = PG_GETARG_INT16(3);
  359. t_bits_str = PG_ARGISNULL(4) ? NULL :
  360. text_to_cstring(PG_GETARG_TEXT_PP(4));
  361. if (PG_NARGS() >= 6)
  362. do_detoast = PG_GETARG_BOOL(5);
  363. if (!superuser())
  364. ereport(ERROR,
  365. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  366. errmsg("must be superuser to use raw page functions")));
  367. if (!raw_data)
  368. PG_RETURN_NULL();
  369. /*
  370. * Convert t_bits string back to the bits8 array as represented in the
  371. * tuple header.
  372. */
  373. if (t_infomask & HEAP_HASNULL)
  374. {
  375. int bits_str_len;
  376. int bits_len;
  377. bits_len = BITMAPLEN(t_infomask2 & HEAP_NATTS_MASK) * BITS_PER_BYTE;
  378. if (!t_bits_str)
  379. ereport(ERROR,
  380. (errcode(ERRCODE_DATA_CORRUPTED),
  381. errmsg("argument of t_bits is null, but it is expected to be null and %d character long",
  382. bits_len)));
  383. bits_str_len = strlen(t_bits_str);
  384. if (bits_len != bits_str_len)
  385. ereport(ERROR,
  386. (errcode(ERRCODE_DATA_CORRUPTED),
  387. errmsg("unexpected length of t_bits %u, expected %d",
  388. bits_str_len, bits_len)));
  389. /* do the conversion */
  390. t_bits = text_to_bits(t_bits_str, bits_str_len);
  391. }
  392. else
  393. {
  394. if (t_bits_str)
  395. ereport(ERROR,
  396. (errcode(ERRCODE_DATA_CORRUPTED),
  397. errmsg("t_bits string is expected to be NULL, but instead it is %zu bytes length",
  398. strlen(t_bits_str))));
  399. }
  400. /* Split tuple data */
  401. res = tuple_data_split_internal(relid, (char *) raw_data + VARHDRSZ,
  402. VARSIZE(raw_data) - VARHDRSZ,
  403. t_infomask, t_infomask2, t_bits,
  404. do_detoast);
  405. if (t_bits)
  406. pfree(t_bits);
  407. PG_RETURN_ARRAYTYPE_P(res);
  408. }