diff --git a/contrib/pageinspect/expected/gist.out b/contrib/pageinspect/expected/gist.out index 460bef3037..b7767cdb42 100644 --- a/contrib/pageinspect/expected/gist.out +++ b/contrib/pageinspect/expected/gist.out @@ -107,4 +107,21 @@ SELECT gist_page_opaque_info(decode(repeat('00', :block_size), 'hex')); (1 row) +-- Test gist_page_items with pages of an index containing non-key columns +ALTER TABLE test_gist ADD COLUMN i int; +CREATE INDEX test_gist_idx_inc ON test_gist USING gist (p) INCLUDE (t, i); +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx_inc', 0), 'test_gist_idx_inc') + WHERE itemoffset = 1; + itemoffset | ctid | itemlen | dead | keys +------------+-----------+---------+------+----------------- + 1 | (1,65535) | 40 | f | (p)=((135,135)) +(1 row) + +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx_inc', 1), 'test_gist_idx_inc') + WHERE itemoffset = 1; + itemoffset | ctid | itemlen | dead | keys +------------+-------+---------+------+---------------------------------------------- + 1 | (0,1) | 56 | f | (p) INCLUDE (t, i)=((1,1)) INCLUDE (1, null) +(1 row) + DROP TABLE test_gist; diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c index 3dca7f1318..422290311c 100644 --- a/contrib/pageinspect/gistfuncs.c +++ b/contrib/pageinspect/gistfuncs.c @@ -24,6 +24,8 @@ #include "utils/rel.h" #include "utils/pg_lsn.h" #include "utils/varlena.h" +#include "utils/ruleutils.h" +#include "utils/lsyscache.h" PG_FUNCTION_INFO_V1(gist_page_opaque_info); PG_FUNCTION_INFO_V1(gist_page_items); @@ -198,9 +200,12 @@ gist_page_items(PG_FUNCTION_ARGS) Oid indexRelid = PG_GETARG_OID(1); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Relation indexRel; + TupleDesc tupdesc; Page page; + uint16 flagbits; OffsetNumber offset; OffsetNumber maxoff = InvalidOffsetNumber; + char *index_columns; if (!superuser()) ereport(ERROR, @@ -226,6 +231,19 @@ gist_page_items(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } + flagbits = GistPageGetOpaque(page)->flags; + if (flagbits & F_LEAF) + { + tupdesc = RelationGetDescr(indexRel); + index_columns = pg_get_indexdef_columns_with_included(indexRelid, true); + } + else + { + tupdesc = CreateTupleDescCopy(RelationGetDescr(indexRel)); + tupdesc->natts = IndexRelationGetNumberOfKeyAttributes(indexRel); + index_columns = pg_get_indexdef_columns(indexRelid, true); + } + /* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */ if (GistPageIsDeleted(page)) elog(NOTICE, "page is deleted"); @@ -242,7 +260,8 @@ gist_page_items(PG_FUNCTION_ARGS) IndexTuple itup; Datum itup_values[INDEX_MAX_KEYS]; bool itup_isnull[INDEX_MAX_KEYS]; - char *key_desc; + StringInfoData buf; + int i; id = PageGetItemId(page, offset); @@ -251,7 +270,7 @@ gist_page_items(PG_FUNCTION_ARGS) itup = (IndexTuple) PageGetItem(page, id); - index_deform_tuple(itup, RelationGetDescr(indexRel), + index_deform_tuple(itup, tupdesc, itup_values, itup_isnull); memset(nulls, 0, sizeof(nulls)); @@ -261,9 +280,56 @@ gist_page_items(PG_FUNCTION_ARGS) values[2] = Int32GetDatum((int) IndexTupleSize(itup)); values[3] = BoolGetDatum(ItemIdIsDead(id)); - key_desc = BuildIndexValueDescription(indexRel, itup_values, itup_isnull); - if (key_desc) - values[4] = CStringGetTextDatum(key_desc); + if (index_columns) + { + initStringInfo(&buf); + appendStringInfo(&buf, "(%s)=(", index_columns); + + for (i = 0; i < tupdesc->natts; i++) + { + char *val; + + if (itup_isnull[i]) + val = "null"; + else + { + Oid foutoid; + bool typisvarlena; + + if (i < IndexRelationGetNumberOfKeyAttributes(indexRel)) + { + /* + * Use rd_opcintype for key columns as in + * BuildIndexValueDescription(). + */ + getTypeOutputInfo(indexRel->rd_opcintype[i], + &foutoid, &typisvarlena); + } + else + { + getTypeOutputInfo(tupdesc->attrs[i].atttypid, + &foutoid, &typisvarlena); + } + val = OidOutputFunctionCall(foutoid, itup_values[i]); + } + + if (i == IndexRelationGetNumberOfKeyAttributes(indexRel)) + { + appendStringInfoString(&buf, ") INCLUDE ("); + } + else if (i > 0) + { + appendStringInfoString(&buf, ", "); + } + + appendStringInfoString(&buf, val); + } + + appendStringInfoChar(&buf, ')'); + + values[4] = CStringGetTextDatum(buf.data); + nulls[4] = false; + } else { values[4] = (Datum) 0; diff --git a/contrib/pageinspect/sql/gist.sql b/contrib/pageinspect/sql/gist.sql index 4787b784a4..398af01c66 100644 --- a/contrib/pageinspect/sql/gist.sql +++ b/contrib/pageinspect/sql/gist.sql @@ -52,4 +52,12 @@ SELECT gist_page_items_bytea(decode(repeat('00', :block_size), 'hex')); SELECT gist_page_items(decode(repeat('00', :block_size), 'hex'), 'test_gist_idx'::regclass); SELECT gist_page_opaque_info(decode(repeat('00', :block_size), 'hex')); +-- Test gist_page_items with pages of an index containing non-key columns +ALTER TABLE test_gist ADD COLUMN i int; +CREATE INDEX test_gist_idx_inc ON test_gist USING gist (p) INCLUDE (t, i); +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx_inc', 0), 'test_gist_idx_inc') + WHERE itemoffset = 1; +SELECT * FROM gist_page_items(get_raw_page('test_gist_idx_inc', 1), 'test_gist_idx_inc') + WHERE itemoffset = 1; + DROP TABLE test_gist; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 60f9d08d5d..71f650868a 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -1215,6 +1215,20 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty) prettyFlags, false); } +/* Internal version that reports also the non-key-column definitions */ +char * +pg_get_indexdef_columns_with_included(Oid indexrelid, bool pretty) +{ + int prettyFlags; + + prettyFlags = GET_PRETTY_FLAGS(pretty); + + return pg_get_indexdef_worker(indexrelid, 0, NULL, + true, false, + false, false, + prettyFlags, false); +} + /* * Internal workhorse to decompile an index definition. * diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h index 1a42d9f39b..d221724cc1 100644 --- a/src/include/utils/ruleutils.h +++ b/src/include/utils/ruleutils.h @@ -23,6 +23,8 @@ struct PlannedStmt; extern char *pg_get_indexdef_string(Oid indexrelid); extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty); +extern char *pg_get_indexdef_columns_with_included(Oid indexrelid, + bool pretty); extern char *pg_get_querydef(Query *query, bool pretty); extern char *pg_get_partkeydef_columns(Oid relid, bool pretty);