]> granicus.if.org Git - postgresql/commitdiff
pageinspect: Add bt_page_items function with bytea argument
authorPeter Eisentraut <peter_e@gmx.net>
Wed, 5 Apr 2017 03:48:49 +0000 (23:48 -0400)
committerPeter Eisentraut <peter_e@gmx.net>
Wed, 5 Apr 2017 03:52:55 +0000 (23:52 -0400)
Author: Tomas Vondra <tomas.vondra@2ndquadrant.com>
Reviewed-by: Ashutosh Sharma <ashu.coek88@gmail.com>
contrib/pageinspect/btreefuncs.c
contrib/pageinspect/expected/btree.out
contrib/pageinspect/pageinspect--1.5--1.6.sql
contrib/pageinspect/sql/btree.sql
doc/src/sgml/pageinspect.sgml
src/include/access/nbtree.h

index 3d69aa9d37baf9ae55e4a63d18bfa45834d081b4..02440eca477212779b0d0faf050a370d72515904 100644 (file)
@@ -41,6 +41,7 @@
 
 PG_FUNCTION_INFO_V1(bt_metap);
 PG_FUNCTION_INFO_V1(bt_page_items);
+PG_FUNCTION_INFO_V1(bt_page_items_bytea);
 PG_FUNCTION_INFO_V1(bt_page_stats);
 
 #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX)
@@ -235,14 +236,6 @@ bt_page_stats(PG_FUNCTION_ARGS)
        PG_RETURN_DATUM(result);
 }
 
-/*-------------------------------------------------------
- * bt_page_items()
- *
- * Get IndexTupleData set in a btree page
- *
- * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
- *-------------------------------------------------------
- */
 
 /*
  * cross-call data structure for SRF
@@ -253,14 +246,72 @@ struct user_args
        OffsetNumber offset;
 };
 
+/*-------------------------------------------------------
+ * bt_page_print_tuples()
+ *
+ * Form a tuple describing index tuple at a given offset
+ * ------------------------------------------------------
+ */
+static Datum
+bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset)
+{
+       char       *values[6];
+       HeapTuple       tuple;
+       ItemId          id;
+       IndexTuple      itup;
+       int                     j;
+       int                     off;
+       int                     dlen;
+       char       *dump;
+       char       *ptr;
+
+       id = PageGetItemId(page, offset);
+
+       if (!ItemIdIsValid(id))
+               elog(ERROR, "invalid ItemId");
+
+       itup = (IndexTuple) PageGetItem(page, id);
+
+       j = 0;
+       values[j++] = psprintf("%d", offset);
+       values[j++] = psprintf("(%u,%u)",
+                                                  ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
+                                                  ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
+       values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
+       values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
+       values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
+
+       ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
+       dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
+       dump = palloc0(dlen * 3 + 1);
+       values[j] = dump;
+       for (off = 0; off < dlen; off++)
+       {
+               if (off > 0)
+                       *dump++ = ' ';
+               sprintf(dump, "%02x", *(ptr + off) & 0xff);
+               dump += 2;
+       }
+
+       tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
+
+       return HeapTupleGetDatum(tuple);
+}
+
+/*-------------------------------------------------------
+ * bt_page_items()
+ *
+ * Get IndexTupleData set in a btree page
+ *
+ * Usage: SELECT * FROM bt_page_items('t1_pkey', 1);
+ *-------------------------------------------------------
+ */
 Datum
 bt_page_items(PG_FUNCTION_ARGS)
 {
        text       *relname = PG_GETARG_TEXT_PP(0);
        uint32          blkno = PG_GETARG_UINT32(1);
        Datum           result;
-       char       *values[6];
-       HeapTuple       tuple;
        FuncCallContext *fctx;
        MemoryContext mctx;
        struct user_args *uargs;
@@ -345,47 +396,8 @@ bt_page_items(PG_FUNCTION_ARGS)
 
        if (fctx->call_cntr < fctx->max_calls)
        {
-               ItemId          id;
-               IndexTuple      itup;
-               int                     j;
-               int                     off;
-               int                     dlen;
-               char       *dump;
-               char       *ptr;
-
-               id = PageGetItemId(uargs->page, uargs->offset);
-
-               if (!ItemIdIsValid(id))
-                       elog(ERROR, "invalid ItemId");
-
-               itup = (IndexTuple) PageGetItem(uargs->page, id);
-
-               j = 0;
-               values[j++] = psprintf("%d", uargs->offset);
-               values[j++] = psprintf("(%u,%u)",
-                                                          ItemPointerGetBlockNumberNoCheck(&itup->t_tid),
-                                                       ItemPointerGetOffsetNumberNoCheck(&itup->t_tid));
-               values[j++] = psprintf("%d", (int) IndexTupleSize(itup));
-               values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f');
-               values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f');
-
-               ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
-               dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info);
-               dump = palloc0(dlen * 3 + 1);
-               values[j] = dump;
-               for (off = 0; off < dlen; off++)
-               {
-                       if (off > 0)
-                               *dump++ = ' ';
-                       sprintf(dump, "%02x", *(ptr + off) & 0xff);
-                       dump += 2;
-               }
-
-               tuple = BuildTupleFromCStrings(fctx->attinmeta, values);
-               result = HeapTupleGetDatum(tuple);
-
-               uargs->offset = uargs->offset + 1;
-
+               result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
+               uargs->offset++;
                SRF_RETURN_NEXT(fctx, result);
        }
        else
@@ -396,6 +408,90 @@ bt_page_items(PG_FUNCTION_ARGS)
        }
 }
 
+/*-------------------------------------------------------
+ * bt_page_items_bytea()
+ *
+ * Get IndexTupleData set in a btree page
+ *
+ * Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1));
+ *-------------------------------------------------------
+ */
+
+Datum
+bt_page_items_bytea(PG_FUNCTION_ARGS)
+{
+       bytea      *raw_page = PG_GETARG_BYTEA_P(0);
+       Datum           result;
+       FuncCallContext *fctx;
+       struct user_args *uargs;
+       int                     raw_page_size;
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                (errmsg("must be superuser to use pageinspect functions"))));
+
+       if (SRF_IS_FIRSTCALL())
+       {
+               BTPageOpaque opaque;
+               MemoryContext mctx;
+               TupleDesc       tupleDesc;
+
+               raw_page_size = VARSIZE(raw_page) - VARHDRSZ;
+
+               if (raw_page_size < SizeOfPageHeaderData)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                 errmsg("input page too small (%d bytes)", raw_page_size)));
+
+               fctx = SRF_FIRSTCALL_INIT();
+               mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
+
+               uargs = palloc(sizeof(struct user_args));
+
+               uargs->page = VARDATA(raw_page);
+
+               uargs->offset = FirstOffsetNumber;
+
+               opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
+
+               if (P_ISMETA(opaque))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("block is a meta page")));
+
+               if (P_ISDELETED(opaque))
+                       elog(NOTICE, "page is deleted");
+
+               fctx->max_calls = PageGetMaxOffsetNumber(uargs->page);
+
+               /* Build a tuple descriptor for our result type */
+               if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE)
+                       elog(ERROR, "return type must be a row type");
+
+               fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc);
+
+               fctx->user_fctx = uargs;
+
+               MemoryContextSwitchTo(mctx);
+       }
+
+       fctx = SRF_PERCALL_SETUP();
+       uargs = fctx->user_fctx;
+
+       if (fctx->call_cntr < fctx->max_calls)
+       {
+               result = bt_page_print_tuples(fctx, uargs->page, uargs->offset);
+               uargs->offset++;
+               SRF_RETURN_NEXT(fctx, result);
+       }
+       else
+       {
+               pfree(uargs);
+               SRF_RETURN_DONE(fctx);
+       }
+}
+
 
 /* ------------------------------------------------
  * bt_metap()
index 82a49e3d6c75560eb6bbc94f2ca36db4917e83c2..67b103add3fef7c2dbb952716cbaa8d41bfacc1e 100644 (file)
@@ -42,4 +42,17 @@ data       | 01 00 00 00 00 00 00 01
 
 SELECT * FROM bt_page_items('test1_a_idx', 2);
 ERROR:  block number out of range
+SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
+ERROR:  block is a meta page
+SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
+-[ RECORD 1 ]-----------------------
+itemoffset | 1
+ctid       | (0,1)
+itemlen    | 16
+nulls      | f
+vars       | f
+data       | 01 00 00 00 00 00 00 01
+
+SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
+ERROR:  block number 2 is out of range for relation "test1_a_idx"
 DROP TABLE test1;
index 6ac2a8011d80a1af6e178029685adcdf634d3be2..c4356288db3652219cccbee86ee3821251eba248 100644 (file)
@@ -83,3 +83,17 @@ CREATE FUNCTION page_checksum(IN page bytea, IN blkno int4)
 RETURNS smallint
 AS 'MODULE_PATHNAME', 'page_checksum'
 LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- bt_page_items_bytea()
+--
+CREATE FUNCTION bt_page_items(IN page bytea,
+    OUT itemoffset smallint,
+    OUT ctid tid,
+    OUT itemlen smallint,
+    OUT nulls bool,
+    OUT vars bool,
+    OUT data text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'bt_page_items_bytea'
+LANGUAGE C STRICT PARALLEL SAFE;
index 1eafc3249ccf2e48b55e98000849c859934f30fa..8eac64c7b3cb7827160646e38f60819f30e1b43d 100644 (file)
@@ -14,4 +14,8 @@ SELECT * FROM bt_page_items('test1_a_idx', 0);
 SELECT * FROM bt_page_items('test1_a_idx', 1);
 SELECT * FROM bt_page_items('test1_a_idx', 2);
 
+SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
+SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
+SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
+
 DROP TABLE test1;
index c87b160bf48649c5ceabfd348d9a5aad9975cbda..ccdaf3e0aca3b699f0af0e452134a90e52e47f52 100644 (file)
@@ -333,6 +333,38 @@ test=# SELECT * FROM bt_page_items('pg_cast_oid_index', 1);
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>bt_page_items(page bytea) returns setof record</function>
+     <indexterm>
+      <primary>bt_page_items</primary>
+     </indexterm>
+    </term>
+
+    <listitem>
+     <para>
+      It is also possible to pass a page to <function>bt_page_items</function>
+      as a <type>bytea</> value.  A page image obtained
+      with <function>get_raw_page</function> should be passed as argument.  So
+      the last example could also be rewritten like this:
+<screen>
+test=# SELECT * FROM bt_page_items(get_raw_page('pg_cast_oid_index', 1));
+ itemoffset |  ctid   | itemlen | nulls | vars |    data
+------------+---------+---------+-------+------+-------------
+          1 | (0,1)   |      12 | f     | f    | 23 27 00 00
+          2 | (0,2)   |      12 | f     | f    | 24 27 00 00
+          3 | (0,3)   |      12 | f     | f    | 25 27 00 00
+          4 | (0,4)   |      12 | f     | f    | 26 27 00 00
+          5 | (0,5)   |      12 | f     | f    | 27 27 00 00
+          6 | (0,6)   |      12 | f     | f    | 28 27 00 00
+          7 | (0,7)   |      12 | f     | f    | 29 27 00 00
+          8 | (0,8)   |      12 | f     | f    | 2a 27 00 00
+</screen>
+      All the other details are the same as explained in the previous item.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </sect2>
 
index f9304dbe1d595797535d64a43c835acfcf87bcbb..15771ce9e0b7ed11f129c31f9e7ba30d3342d78b 100644 (file)
@@ -176,6 +176,7 @@ typedef struct BTMetaPageData
 #define P_ISLEAF(opaque)               ((opaque)->btpo_flags & BTP_LEAF)
 #define P_ISROOT(opaque)               ((opaque)->btpo_flags & BTP_ROOT)
 #define P_ISDELETED(opaque)            ((opaque)->btpo_flags & BTP_DELETED)
+#define P_ISMETA(opaque)               ((opaque)->btpo_flags & BTP_META)
 #define P_ISHALFDEAD(opaque)   ((opaque)->btpo_flags & BTP_HALF_DEAD)
 #define P_IGNORE(opaque)               ((opaque)->btpo_flags & (BTP_DELETED|BTP_HALF_DEAD))
 #define P_HAS_GARBAGE(opaque)  ((opaque)->btpo_flags & BTP_HAS_GARBAGE)