On 06.01.26 17:44, Kirill Reshke wrote:
On Tue, 6 Jan 2026 at 21:28, Kirill Reshke <[email protected]> wrote:

On Mon, 5 Jan 2026 at 13:39, Andrey Borodin <[email protected]> wrote:



On 4 Jan 2026, at 00:25, Kirill Reshke <[email protected]> wrote:

PFA v6

Would it be theoretically possible to unite functions for different GIN page 
types?
e.g. merge gin_entrypage_items + gin_datapage_items -> gin_tree_items? Or is it 
an awkward API?

For this, I borrowed this design from HASH and BRIN pageinspect
implementation. For them, we have one function-per-page-type. So,
maybe we can have dynamic schema here, but I don't see this as an
improvement to design.

--
Best regards,
Kirill Reshke

CF bot did like trailing whitespace in regression output files,
posting v8 with this issue fixed.

I noticed that the newly added C code could use a bit of modernization. See attached patch for some suggestions. I only treated the new gin_entrypage_items(); the new gin_datapage_items() could use mostly the same changes. One of the main changes is to push many local variables to smaller scopes. Also some changes in ereport parentheses style, palloc style, some whitespace changes. (The whole patch also needs pgindent treatment.)
From b424062035a43c894cf6d11b854a917be0cad6d8 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Wed, 7 Jan 2026 17:37:05 +0100
Subject: [PATCH] some code cleanup

---
 contrib/pageinspect/ginfuncs.c | 95 ++++++++++++++++------------------
 1 file changed, 44 insertions(+), 51 deletions(-)

diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c
index 3cd5e4c0e65..4294b640a9a 100644
--- a/contrib/pageinspect/ginfuncs.c
+++ b/contrib/pageinspect/ginfuncs.c
@@ -191,17 +191,16 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
        Oid                             indexRelid = PG_GETARG_OID(1);
        ReturnSetInfo   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
        Relation                indexRel;
-       OffsetNumber    maxoff, offset;
+       OffsetNumber    maxoff;
        TupleDesc               tupdesc;
-       bool                    oneCol;
        Page                    page;
        GinPageOpaque   opaq;
        StringInfoData  buf;
 
        if (!superuser())
                ereport(ERROR,
-                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                                errmsg("must be superuser to use raw page 
functions")));
+                               errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                               errmsg("must be superuser to use raw page 
functions"));
 
        InitMaterializedSRF(fcinfo, 0);
 
@@ -210,9 +209,9 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
 
        if (!IS_INDEX(indexRel) || !IS_GIN(indexRel))
                ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("\"%s\" is not a %s index",
-                                               
RelationGetRelationName(indexRel), "GIN")));
+                               errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                               errmsg("\"%s\" is not a %s index",
+                                          RelationGetRelationName(indexRel), 
"GIN"));
 
        page = get_page_from_raw(raw_page);
 
@@ -224,58 +223,46 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
 
        if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
                ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                       errmsg("input page is not a valid GIN 
entry tree page"),
-                                       errdetail("Expected special size %d, 
got %d.",
-                                                       (int) 
MAXALIGN(sizeof(GinPageOpaqueData)),
-                                                       (int) 
PageGetSpecialSize(page))));
+                               errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                               errmsg("input page is not a valid GIN entry 
tree page"),
+                               errdetail("Expected special size %d, got %d.",
+                                                 (int) 
MAXALIGN(sizeof(GinPageOpaqueData)),
+                                                 PageGetSpecialSize(page)));
 
        opaq = GinPageGetOpaque(page);
 
-
        /* we only support entry tree in this function, check that */
        if (opaq->flags & GIN_META)
                ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                       errmsg("gin_entrypage_items is 
unsupported for metapage")));
+                               errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                               errmsg("gin_entrypage_items is unsupported for 
metapage"));
 
 
        if (opaq->flags & (GIN_LIST | GIN_LIST_FULLROW))
                ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                       errmsg("gin_entrypage_items is 
unsupported for fast list pages")));
+                               errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                               errmsg("gin_entrypage_items is unsupported for 
fast list pages"));
 
 
        if (opaq->flags & GIN_DATA)
                ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                       errmsg("input page is not a GIN entry 
tree page"),
-                                       errhint("This appears to be a GIN 
posting tree page. Please use gin_datapage_items")));
+                               errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                               errmsg("input page is not a GIN entry tree 
page"),
+                               errhint("This appears to be a GIN posting tree 
page. Please use gin_datapage_items."));
 
        initStringInfo(&buf);
        maxoff = PageGetMaxOffsetNumber(page);
 
        tupdesc = RelationGetDescr(indexRel);
-       oneCol = tupdesc->natts == 1;
 
-       for (offset = FirstOffsetNumber;
+       for (OffsetNumber offset = FirstOffsetNumber;
                 offset <= maxoff;
                 offset = OffsetNumberNext(offset))
        {
                OffsetNumber    indAtt;
                Datum           values[4];
-               bool            nulls[4];
-               int                     ndecoded, i;
-               Datum      *tids_datum;
-               ItemPointer items_orig;
-               bool            free_items_orig;
+               bool            nulls[4] = {0};
                Datum           attrVal;
-               Oid                     foutoid;
-               bool            typisvarlena;
-               Oid                     typoid;
-               char*           value;
-               bool            nq;
-               char*           tmp;
                bool            isnull;
                IndexTuple      idxtuple;
                ItemId          iid = PageGetItemId(page, offset);
@@ -285,20 +272,17 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
 
                idxtuple = (IndexTuple) PageGetItem(page, iid);
 
-               memset(nulls, 0, sizeof(nulls));
-
                values[0] = UInt16GetDatum(offset);
 
-               if (oneCol)
+               if (tupdesc->natts == 1)
                {
                        indAtt = FirstOffsetNumber;
                        /* here we can safely reuse pg_class's tuple 
descriptor. */
-                       attrVal = index_getattr(idxtuple, FirstOffsetNumber, 
tupdesc,
-                                                       &isnull);
+                       attrVal = index_getattr(idxtuple, FirstOffsetNumber, 
tupdesc, &isnull);
                        if (isnull)
                                ereport(ERROR,
-                                               
(errcode(ERRCODE_INDEX_CORRUPTED),
-                                                       errmsg("invalid gin 
entry page tuple at offset %d", offset)));
+                                               
errcode(ERRCODE_INDEX_CORRUPTED),
+                                               errmsg("invalid gin entry page 
tuple at offset %d", offset));
                }
                else
                {
@@ -313,14 +297,13 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
 
                        /* orig tuple reuse is safe */
 
-                       res = index_getattr(idxtuple, FirstOffsetNumber, 
tupdesc,
-                                                               &isnull);
+                       res = index_getattr(idxtuple, FirstOffsetNumber, 
tupdesc, &isnull);
 
                        /* we do not expect null for first attr in multi-column 
GIN */
                        if (isnull)
                                ereport(ERROR,
-                                               
(errcode(ERRCODE_INDEX_CORRUPTED),
-                                                       errmsg("invalid gin 
entry page tuple at offset %d", offset)));
+                                               
errcode(ERRCODE_INDEX_CORRUPTED),
+                                               errmsg("invalid gin entry page 
tuple at offset %d", offset));
 
                        indAtt = DatumGetUInt16(res);
 
@@ -346,16 +329,22 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
 
                appendStringInfo(&buf, "%s=", 
quote_identifier(TupleDescAttr(tupdesc, indAtt - 1)->attname.data));
 
-               if (!isnull) {
+               if (!isnull)
+               {
+                       Oid                     foutoid;
+                       bool            typisvarlena;
+                       Oid                     typoid;
+                       char*           value;
+                       bool            nq;
+
                        /* Most of this is copied from record_out(). */
                        typoid = TupleDescAttr(tupdesc, indAtt - 1)->atttypid;
                        getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
                        value = OidOutputFunctionCall(foutoid, attrVal);
 
-
                        /* Check whether we need double quotes for this value */
                        nq = (value[0] == '\0');        /* force quotes for 
empty string */
-                       for (tmp = value; *tmp; tmp++)
+                       for (const char *tmp = value; *tmp; tmp++)
                        {
                                char            ch = *tmp;
 
@@ -371,7 +360,7 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
                        /* And emit the string */
                        if (nq)
                                appendStringInfoCharMacro(&buf, '"');
-                       for (tmp = value; *tmp; tmp++)
+                       for (const char *tmp = value; *tmp; tmp++)
                        {
                                char            ch = *tmp;
 
@@ -387,7 +376,6 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
                        appendStringInfo(&buf, "NULL");
                }
 
-
                values[3] = CStringGetTextDatum(buf.data);
                resetStringInfo(&buf);
 
@@ -398,6 +386,11 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
                }
                else
                {
+                       int                     ndecoded;
+                       Datum      *tids_datum;
+                       ItemPointer items_orig;
+                       bool            free_items_orig;
+
                        values[1] = ItemPointerGetDatum(&idxtuple->t_tid);
                        /* Get list of item pointers from the tuple. */
                        if (GinItupIsCompressed(idxtuple))
@@ -412,8 +405,8 @@ gin_entrypage_items(PG_FUNCTION_ARGS)
                                free_items_orig = false;
                        }
 
-                       tids_datum = (Datum *) palloc(ndecoded * sizeof(Datum));
-                       for (i = 0; i < ndecoded; i++)
+                       tids_datum = palloc_array(Datum, ndecoded);
+                       for (int i = 0; i < ndecoded; i++)
                                tids_datum[i] = 
ItemPointerGetDatum(&items_orig[i]);
                        values[2] = 
PointerGetDatum(construct_array_builtin(tids_datum, ndecoded, TIDOID));
 
-- 
2.52.0

Reply via email to