Re: Add RESPECT/IGNORE NULLS and FROM FIRST/LAST options - Mailing list pgsql-hackers

From Tatsuo Ishii
Subject Re: Add RESPECT/IGNORE NULLS and FROM FIRST/LAST options
Date
Msg-id 20250120.085517.1437455666347613522.ishii@postgresql.org
Whole thread Raw
In response to Add RESPECT/IGNORE NULLS and FROM FIRST/LAST options  (Oliver Ford <ojford@gmail.com>)
Responses Re: Add RESPECT/IGNORE NULLS and FROM FIRST/LAST options
List pgsql-hackers
Thanks for updating the patch.

>> It seems you allow to use IGNORE NULLS for all window functions. If
>> the case, you should explicitely stat that in the docs. Otherwise
>> users will be confused because;
> 
> The latest version restricts it to lag, lead, first_value, last_value,
> and nth_value. We can extend it in a subsequent patch if there's
> demand?

The restriction is required by the SQL standard. So I don't think we
need to extend to other window functions.

>> I take a look at the patch and noticed that following functions have
>> no comments on what they are doing and what are the arguments. Please
>> look into other functions in nodeWindowAgg.c and add appropriate
>> comments to those functions.
> 
> Latest version has more comments and should be in the standard coding style.

Still I see non standard coding stiles and indentations. See attached
patch for nodeWindowAgg.c, which is fixed by pgindent, for
example. (Other files may need fixing too).

>> I also notice that you have an array in memory which records non-null
>> row positions in a partition. The position is represented in int64,
>> which means 1 entry consumes 8 bytes. If my understanding is correct,
>> the array continues to grow up to the partition size. Also the array
>> is created for each window function (is it really necessary?). I worry
>> about this because it might consume excessive memory for big
>> partitions.
> 
> It's an int64 because it stores the abs_pos/mark_pos which are int64.
> Keeping an array for each function is needed for the mark optimization
> to work correctly.

Ok.

Here are some comments regarding the patch:

(1) I noticed that ignorenulls_getfuncarginframe() does not take
account EXCLUSION frame options. The code path is in
WinGetFuncArgInFrame():

            /*
             * Account for exclusion option if one is active, but advance only
             * abs_pos not mark_pos.  This prevents changes of the current
             * row's peer group from resulting in trying to fetch a row before
             * some previous mark position.
:
:

I guess ignorenulls_getfuncarginframe() was created by modifying
WinGetFuncArgInFrame() so I don't see the reason why
ignorenulls_getfuncarginframe() does not take account EXCLUSION frame
options.

(2) New member ignore_nulls are added to some structs. Its value is 0,
1 or -1. It's better to use a DEFINE for the value of ignore_nulls,
rather than 0, 1, or -1.

Best reagards,
--
Tatsuo Ishii
SRA OSS K.K.
English: http://www.sraoss.co.jp/index_en/
Japanese:http://www.sraoss.co.jp

diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index e1117857dc0..520e7e1bfcb 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2624,7 +2624,10 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
             elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
                  wfunc->winref, node->winref);
 
-        /* Look for a previous duplicate window function, which needs the same ignore_nulls value */
+        /*
+         * Look for a previous duplicate window function, which needs the same
+         * ignore_nulls value
+         */
         for (i = 0; i <= wfuncno; i++)
         {
             if (equal(wfunc, perfunc[i].wfunc) &&
@@ -3379,8 +3382,8 @@ increment_nonnulls(WindowObject winobj, int64 pos)
         winobj->nonnulls_size *= 2;
         winobj->win_nonnulls =
             repalloc_array(winobj->win_nonnulls,
-                            int64,
-                            winobj->nonnulls_size);
+                           int64,
+                           winobj->nonnulls_size);
     }
     winobj->win_nonnulls[winobj->nonnulls_len] = pos;
     winobj->nonnulls_len++;
@@ -3394,7 +3397,8 @@ increment_nonnulls(WindowObject winobj, int64 pos)
 static Datum
 ignorenulls_getfuncarginpartition(WindowObject winobj, int argno,
                                   int relpos, int seektype, bool set_mark,
-                                  bool *isnull, bool *isout) {
+                                  bool *isnull, bool *isout)
+{
     WindowAggState *winstate;
     ExprContext *econtext;
     TupleTableSlot *slot;
@@ -3416,27 +3420,27 @@ ignorenulls_getfuncarginpartition(WindowObject winobj, int argno,
 
     switch (seektype)
     {
-    case WINDOW_SEEK_CURRENT:
-        abs_pos = winstate->currentpos;
-        break;
-    case WINDOW_SEEK_HEAD:
-        abs_pos = 0;
-        break;
-    case WINDOW_SEEK_TAIL:
-        spool_tuples(winstate, -1);
-        abs_pos = winstate->spooled_rows - 1;
-        break;
-    default:
-        elog(ERROR, "unrecognized window seek type: %d", seektype);
-        abs_pos = 0; /* keep compiler quiet */
-        break;
+        case WINDOW_SEEK_CURRENT:
+            abs_pos = winstate->currentpos;
+            break;
+        case WINDOW_SEEK_HEAD:
+            abs_pos = 0;
+            break;
+        case WINDOW_SEEK_TAIL:
+            spool_tuples(winstate, -1);
+            abs_pos = winstate->spooled_rows - 1;
+            break;
+        default:
+            elog(ERROR, "unrecognized window seek type: %d", seektype);
+            abs_pos = 0;        /* keep compiler quiet */
+            break;
     }
 
     if (forward == -1)
         goto check_partition;
 
     /* if we're moving forward, store previous rows */
-    for (i=0; i < winobj->nonnulls_len; ++i)
+    for (i = 0; i < winobj->nonnulls_len; ++i)
     {
         if (winobj->win_nonnulls[i] > abs_pos)
         {
@@ -3448,7 +3452,7 @@ ignorenulls_getfuncarginpartition(WindowObject winobj, int argno,
                     *isout = false;
                 window_gettupleslot(winobj, abs_pos, slot);
                 econtext->ecxt_outertuple = slot;
-                return ExecEvalExpr((ExprState *)list_nth(winobj->argstates, argno),
+                return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
                                     econtext, isnull);
             }
         }
@@ -3465,13 +3469,13 @@ check_partition:
             if (isout)
                 *isout = true;
             *isnull = true;
-            return (Datum)0;
+            return (Datum) 0;
         }
 
         if (isout)
             *isout = false;
         econtext->ecxt_outertuple = slot;
-        datum = ExecEvalExpr((ExprState *)list_nth(winobj->argstates, argno),
+        datum = ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
                              econtext, isnull);
 
         if (!*isnull)
@@ -3494,7 +3498,8 @@ check_partition:
 static Datum
 ignorenulls_getfuncarginframe(WindowObject winobj, int argno,
                               int relpos, int seektype, bool set_mark,
-                              bool *isnull, bool *isout) {
+                              bool *isnull, bool *isout)
+{
     WindowAggState *winstate;
     ExprContext *econtext;
     TupleTableSlot *slot;
@@ -3511,7 +3516,7 @@ ignorenulls_getfuncarginframe(WindowObject winobj, int argno,
     winstate = winobj->winstate;
     econtext = winstate->ss.ps.ps_ExprContext;
     slot = winstate->temp_slot_1;
-    datum = (Datum)0;
+    datum = (Datum) 0;
     notnull_offset = 0;
     notnull_relpos = abs(relpos);
 
@@ -3549,70 +3554,72 @@ ignorenulls_getfuncarginframe(WindowObject winobj, int argno,
      */
     for (i = 0; i < winobj->nonnulls_len; ++i)
     {
-            int inframe;
-            if (winobj->win_nonnulls[i] < winobj->markpos)
-                continue;
-            if (!window_gettupleslot(winobj, winobj->win_nonnulls[i], slot))
-                continue;
+        int            inframe;
 
-            inframe = row_is_in_frame(winstate, winobj->win_nonnulls[i], slot);
-            if (inframe <= 0)
-            {
-                if (inframe == -1 && set_mark)
-                    WinSetMarkPosition(winobj, winobj->win_nonnulls[i]);
-                continue;
-            }
+        if (winobj->win_nonnulls[i] < winobj->markpos)
+            continue;
+        if (!window_gettupleslot(winobj, winobj->win_nonnulls[i], slot))
+            continue;
 
-            abs_pos = winobj->win_nonnulls[i] + 1;
-            ++notnull_offset;
+        inframe = row_is_in_frame(winstate, winobj->win_nonnulls[i], slot);
+        if (inframe <= 0)
+        {
+            if (inframe == -1 && set_mark)
+                WinSetMarkPosition(winobj, winobj->win_nonnulls[i]);
+            continue;
+        }
 
-            if (notnull_offset > notnull_relpos)
-            {
-                if (isout)
+        abs_pos = winobj->win_nonnulls[i] + 1;
+        ++notnull_offset;
+
+        if (notnull_offset > notnull_relpos)
+        {
+            if (isout)
                 *isout = false;
-                econtext->ecxt_outertuple = slot;
-                return ExecEvalExpr((ExprState *)list_nth(winobj->argstates, argno),
-                                    econtext, isnull);
-            }
+            econtext->ecxt_outertuple = slot;
+            return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
+                                econtext, isnull);
+        }
     }
 
 check_frame:
     do
     {
-            int inframe;
-            if (!window_gettupleslot(winobj, abs_pos, slot))
-                goto out_of_frame;
+        int            inframe;
 
-            inframe = row_is_in_frame(winstate, abs_pos, slot);
-            if (inframe == -1)
-                goto out_of_frame;
-            else if (inframe == 0)
-                goto advance;
+        if (!window_gettupleslot(winobj, abs_pos, slot))
+            goto out_of_frame;
 
-            gottuple = window_gettupleslot(winobj, abs_pos, slot);
+        inframe = row_is_in_frame(winstate, abs_pos, slot);
+        if (inframe == -1)
+            goto out_of_frame;
+        else if (inframe == 0)
+            goto advance;
 
-            if (!gottuple)
-            {
-                if (isout)
-                    *isout = true;
-                *isnull = true;
-                return (Datum)0;
-            }
+        gottuple = window_gettupleslot(winobj, abs_pos, slot);
 
+        if (!gottuple)
+        {
             if (isout)
-                *isout = false;
-            econtext->ecxt_outertuple = slot;
-            datum = ExecEvalExpr((ExprState *)list_nth(winobj->argstates, argno),
-                                 econtext, isnull);
+                *isout = true;
+            *isnull = true;
+            return (Datum) 0;
+        }
 
-            if (!*isnull)
-            {
-                ++notnull_offset;
-                increment_nonnulls(winobj, abs_pos);
-            }
+        if (isout)
+            *isout = false;
+        econtext->ecxt_outertuple = slot;
+        datum = ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
+                             econtext, isnull);
+
+        if (!*isnull)
+        {
+            ++notnull_offset;
+            increment_nonnulls(winobj, abs_pos);
+        }
 
 advance:
-            abs_pos += forward;
+        abs_pos += forward;
     } while (notnull_offset <= notnull_relpos);
 
     return datum;
@@ -3660,7 +3667,7 @@ WinGetFuncArgInPartition(WindowObject winobj, int argno,
 
     if (winobj->ignore_nulls == 1 && relpos != 0)
         return ignorenulls_getfuncarginpartition(winobj, argno, relpos, seektype,
-                                                    set_mark, isnull, isout);
+                                                 set_mark, isnull, isout);
 
     switch (seektype)
     {
@@ -3752,7 +3759,7 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
 
     if (winobj->ignore_nulls == 1)
         return ignorenulls_getfuncarginframe(winobj, argno, relpos, seektype,
-                                                set_mark, isnull, isout);
+                                             set_mark, isnull, isout);
 
     switch (seektype)
     {

pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: attndims, typndims still not enforced, but make the value within a sane threshold
Next
From: Hari Krishna Sunder
Date:
Subject: Re: 回复:Re: 回复:Re: speed up pg_upgrade with large number of tables