products/Sources/formale Sprachen/C/MariaDB/sql/share/   (MariaDB Server Version 8.1-8.4©)  Datei vom 1.9.2026 mit Größe 923 kB image not shown  

SSL windowfuncs.c  Interaktion und
Portierbarkeitunbekannt

 
/*-------------------------------------------------------------------------
 *
 * windowfuncs.c
 *   Standard window functions defined in SQL spec.
 *
 * Portions Copyright (c) 2000-2025, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *   src/backend/utils/adt/windowfuncs.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "nodes/parsenodes.h"
#include "nodes/supportnodes.h"
#include "utils/fmgrprotos.h"
#include "windowapi.h"

/*
 * ranking process information
 */

typedef struct rank_context
{
 int64  rank;   /* current rank */
} rank_context;

/*
 * ntile process information
 */

typedef struct
{
 int32  ntile;   /* current result */
 int64  rows_per_bucket; /* row number of current bucket */
 int64  boundary;  /* how many rows should be in the bucket */
 int64  remainder;  /* (total rows) % (bucket num) */
} ntile_context;

static bool rank_up(WindowObject winobj);
static Datum leadlag_common(FunctionCallInfo fcinfo,
       bool forward, bool withoffset, bool withdefault);


/*
 * utility routine for *_rank functions.
 */

static bool
rank_up(WindowObject winobj)
{
 bool  up = false;  /* should rank increase? */
 int64  curpos = WinGetCurrentPosition(winobj);
 rank_context *context;

 context = (rank_context *)
  WinGetPartitionLocalMemory(winobj, sizeof(rank_context));

 if (context->rank == 0)
 {
  /* first call: rank of first row is always 1 */
  Assert(curpos == 0);
  context->rank = 1;
 }
 else
 {
  Assert(curpos > 0);
  /* do current and prior tuples match by ORDER BY clause? */
  if (!WinRowsArePeers(winobj, curpos - 1, curpos))
   up = true;
 }

 /* We can advance the mark, but only *after* access to prior row */
 WinSetMarkPosition(winobj, curpos);

 return up;
}


/*
 * row_number
 * just increment up from 1 until current partition finishes.
 */

Datum
window_row_number(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 int64  curpos = WinGetCurrentPosition(winobj);

 WinSetMarkPosition(winobj, curpos);
 PG_RETURN_INT64(curpos + 1);
}

/*
 * window_row_number_support
 *  prosupport function for window_row_number()
 */

Datum
window_row_number_support(PG_FUNCTION_ARGS)
{
 Node    *rawreq = (Node *) PG_GETARG_POINTER(0);

 if (IsA(rawreq, SupportRequestWFuncMonotonic))
 {
  SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;

  /* row_number() is monotonically increasing */
  req->monotonic = MONOTONICFUNC_INCREASING;
  PG_RETURN_POINTER(req);
 }

 if (IsA(rawreq, SupportRequestOptimizeWindowClause))
 {
  SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;

  /*
   * The frame options can always become "ROWS BETWEEN UNBOUNDED
   * PRECEDING AND CURRENT ROW".  row_number() always just increments by
   * 1 with each row in the partition.  Using ROWS instead of RANGE
   * saves effort checking peer rows during execution.
 */

  req->frameOptions = (FRAMEOPTION_NONDEFAULT |
        FRAMEOPTION_ROWS |
        FRAMEOPTION_START_UNBOUNDED_PRECEDING |
        FRAMEOPTION_END_CURRENT_ROW);

  PG_RETURN_POINTER(req);
 }

 PG_RETURN_POINTER(NULL);
}

/*
 * rank
 * Rank changes when key columns change.
 * The new rank number is the current row number.
 */

Datum
window_rank(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 rank_context *context;
 bool  up;

 up = rank_up(winobj);
 context = (rank_context *)
  WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
 if (up)
  context->rank = WinGetCurrentPosition(winobj) + 1;

 PG_RETURN_INT64(context->rank);
}

/*
 * window_rank_support
 *  prosupport function for window_rank()
 */

Datum
window_rank_support(PG_FUNCTION_ARGS)
{
 Node    *rawreq = (Node *) PG_GETARG_POINTER(0);

 if (IsA(rawreq, SupportRequestWFuncMonotonic))
 {
  SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;

  /* rank() is monotonically increasing */
  req->monotonic = MONOTONICFUNC_INCREASING;
  PG_RETURN_POINTER(req);
 }

 if (IsA(rawreq, SupportRequestOptimizeWindowClause))
 {
  SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;

  /*
   * rank() is coded in such a way that it returns "(COUNT (*) OVER
   * (<opt> RANGE UNBOUNDED PRECEDING) - COUNT (*) OVER (<opt> RANGE
   * CURRENT ROW) + 1)" regardless of the frame options.  We'll set the
   * frame options to "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
   * so they agree with what window_row_number_support() optimized the
   * frame options to be.  Using ROWS instead of RANGE saves from doing
   * peer row checks during execution.
 */

  req->frameOptions = (FRAMEOPTION_NONDEFAULT |
        FRAMEOPTION_ROWS |
        FRAMEOPTION_START_UNBOUNDED_PRECEDING |
        FRAMEOPTION_END_CURRENT_ROW);

  PG_RETURN_POINTER(req);
 }

 PG_RETURN_POINTER(NULL);
}

/*
 * dense_rank
 * Rank increases by 1 when key columns change.
 */

Datum
window_dense_rank(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 rank_context *context;
 bool  up;

 up = rank_up(winobj);
 context = (rank_context *)
  WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
 if (up)
  context->rank++;

 PG_RETURN_INT64(context->rank);
}

/*
 * window_dense_rank_support
 *  prosupport function for window_dense_rank()
 */

Datum
window_dense_rank_support(PG_FUNCTION_ARGS)
{
 Node    *rawreq = (Node *) PG_GETARG_POINTER(0);

 if (IsA(rawreq, SupportRequestWFuncMonotonic))
 {
  SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;

  /* dense_rank() is monotonically increasing */
  req->monotonic = MONOTONICFUNC_INCREASING;
  PG_RETURN_POINTER(req);
 }

 if (IsA(rawreq, SupportRequestOptimizeWindowClause))
 {
  SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;

  /*
   * dense_rank() is unaffected by the frame options.  Here we set the
   * frame options to match what's done in row_number's support
   * function.  Using ROWS instead of RANGE (the default) saves the
   * executor from having to check for peer rows.
 */

  req->frameOptions = (FRAMEOPTION_NONDEFAULT |
        FRAMEOPTION_ROWS |
        FRAMEOPTION_START_UNBOUNDED_PRECEDING |
        FRAMEOPTION_END_CURRENT_ROW);

  PG_RETURN_POINTER(req);
 }

 PG_RETURN_POINTER(NULL);
}

/*
 * percent_rank
 * return fraction between 0 and 1 inclusive,
 * which is described as (RK - 1) / (NR - 1), where RK is the current row's
 * rank and NR is the total number of rows, per spec.
 */

Datum
window_percent_rank(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 rank_context *context;
 bool  up;
 int64  totalrows = WinGetPartitionRowCount(winobj);

 Assert(totalrows > 0);

 up = rank_up(winobj);
 context = (rank_context *)
  WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
 if (up)
  context->rank = WinGetCurrentPosition(winobj) + 1;

 /* return zero if there's only one row, per spec */
 if (totalrows <= 1)
  PG_RETURN_FLOAT8(0.0);

 PG_RETURN_FLOAT8((float8) (context->rank - 1) / (float8) (totalrows - 1));
}

/*
 * window_percent_rank_support
 *  prosupport function for window_percent_rank()
 */

Datum
window_percent_rank_support(PG_FUNCTION_ARGS)
{
 Node    *rawreq = (Node *) PG_GETARG_POINTER(0);

 if (IsA(rawreq, SupportRequestWFuncMonotonic))
 {
  SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;

  /* percent_rank() is monotonically increasing */
  req->monotonic = MONOTONICFUNC_INCREASING;
  PG_RETURN_POINTER(req);
 }

 if (IsA(rawreq, SupportRequestOptimizeWindowClause))
 {
  SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;

  /*
   * percent_rank() is unaffected by the frame options.  Here we set the
   * frame options to match what's done in row_number's support
   * function.  Using ROWS instead of RANGE (the default) saves the
   * executor from having to check for peer rows.
 */

  req->frameOptions = (FRAMEOPTION_NONDEFAULT |
        FRAMEOPTION_ROWS |
        FRAMEOPTION_START_UNBOUNDED_PRECEDING |
        FRAMEOPTION_END_CURRENT_ROW);

  PG_RETURN_POINTER(req);
 }

 PG_RETURN_POINTER(NULL);
}


/*
 * cume_dist
 * return fraction between 0 and 1 inclusive,
 * which is described as NP / NR, where NP is the number of rows preceding or
 * peers to the current row, and NR is the total number of rows, per spec.
 */

Datum
window_cume_dist(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 rank_context *context;
 bool  up;
 int64  totalrows = WinGetPartitionRowCount(winobj);

 Assert(totalrows > 0);

 up = rank_up(winobj);
 context = (rank_context *)
  WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
 if (up || context->rank == 1)
 {
  /*
   * The current row is not peer to prior row or is just the first, so
   * count up the number of rows that are peer to the current.
 */

  int64  row;

  context->rank = WinGetCurrentPosition(winobj) + 1;

  /*
   * start from current + 1
 */

  for (row = context->rank; row < totalrows; row++)
  {
   if (!WinRowsArePeers(winobj, row - 1, row))
    break;
   context->rank++;
  }
 }

 PG_RETURN_FLOAT8((float8) context->rank / (float8) totalrows);
}

/*
 * window_cume_dist_support
 *  prosupport function for window_cume_dist()
 */

Datum
window_cume_dist_support(PG_FUNCTION_ARGS)
{
 Node    *rawreq = (Node *) PG_GETARG_POINTER(0);

 if (IsA(rawreq, SupportRequestWFuncMonotonic))
 {
  SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;

  /* cume_dist() is monotonically increasing */
  req->monotonic = MONOTONICFUNC_INCREASING;
  PG_RETURN_POINTER(req);
 }

 if (IsA(rawreq, SupportRequestOptimizeWindowClause))
 {
  SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;

  /*
   * cume_dist() is unaffected by the frame options.  Here we set the
   * frame options to match what's done in row_number's support
   * function.  Using ROWS instead of RANGE (the default) saves the
   * executor from having to check for peer rows.
 */

  req->frameOptions = (FRAMEOPTION_NONDEFAULT |
        FRAMEOPTION_ROWS |
        FRAMEOPTION_START_UNBOUNDED_PRECEDING |
        FRAMEOPTION_END_CURRENT_ROW);

  PG_RETURN_POINTER(req);
 }

 PG_RETURN_POINTER(NULL);
}

/*
 * ntile
 * compute an exact numeric value with scale 0 (zero),
 * ranging from 1 (one) to n, per spec.
 */

Datum
window_ntile(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 ntile_context *context;

 context = (ntile_context *)
  WinGetPartitionLocalMemory(winobj, sizeof(ntile_context));

 if (context->ntile == 0)
 {
  /* first call */
  int64  total;
  int32  nbuckets;
  bool  isnull;

  total = WinGetPartitionRowCount(winobj);
  nbuckets = DatumGetInt32(WinGetFuncArgCurrent(winobj, 0, &isnull));

  /*
   * per spec: If NT is the null value, then the result is the null
   * value.
 */

  if (isnull)
   PG_RETURN_NULL();

  /*
   * per spec: If NT is less than or equal to 0 (zero), then an
   * exception condition is raised.
 */

  if (nbuckets <= 0)
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE),
      errmsg("argument of ntile must be greater than zero")));

  context->ntile = 1;
  context->rows_per_bucket = 0;
  context->boundary = total / nbuckets;
  if (context->boundary <= 0)
   context->boundary = 1;
  else
  {
   /*
    * If the total number is not divisible, add 1 row to leading
    * buckets.
 */

   context->remainder = total % nbuckets;
   if (context->remainder != 0)
    context->boundary++;
  }
 }

 context->rows_per_bucket++;
 if (context->boundary < context->rows_per_bucket)
 {
  /* ntile up */
  if (context->remainder != 0 && context->ntile == context->remainder)
  {
   context->remainder = 0;
   context->boundary -= 1;
  }
  context->ntile += 1;
  context->rows_per_bucket = 1;
 }

 PG_RETURN_INT32(context->ntile);
}

/*
 * window_ntile_support
 *  prosupport function for window_ntile()
 */

Datum
window_ntile_support(PG_FUNCTION_ARGS)
{
 Node    *rawreq = (Node *) PG_GETARG_POINTER(0);

 if (IsA(rawreq, SupportRequestWFuncMonotonic))
 {
  SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;

  /*
   * ntile() is monotonically increasing as the number of buckets cannot
   * change after the first call
 */

  req->monotonic = MONOTONICFUNC_INCREASING;
  PG_RETURN_POINTER(req);
 }

 if (IsA(rawreq, SupportRequestOptimizeWindowClause))
 {
  SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;

  /*
   * ntile() is unaffected by the frame options.  Here we set the frame
   * options to match what's done in row_number's support function.
   * Using ROWS instead of RANGE (the default) saves the executor from
   * having to check for peer rows.
 */

  req->frameOptions = (FRAMEOPTION_NONDEFAULT |
        FRAMEOPTION_ROWS |
        FRAMEOPTION_START_UNBOUNDED_PRECEDING |
        FRAMEOPTION_END_CURRENT_ROW);

  PG_RETURN_POINTER(req);
 }

 PG_RETURN_POINTER(NULL);
}

/*
 * leadlag_common
 * common operation of lead() and lag()
 * For lead() forward is true, whereas for lag() it is false.
 * withoffset indicates we have an offset second argument.
 * withdefault indicates we have a default third argument.
 */

static Datum
leadlag_common(FunctionCallInfo fcinfo,
      bool forward, bool withoffset, bool withdefault)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 int32  offset;
 bool  const_offset;
 Datum  result;
 bool  isnull;
 bool  isout;

 if (withoffset)
 {
  offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
  if (isnull)
   PG_RETURN_NULL();
  const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
 }
 else
 {
  offset = 1;
  const_offset = true;
 }

 result = WinGetFuncArgInPartition(winobj, 0,
           (forward ? offset : -offset),
           WINDOW_SEEK_CURRENT,
           const_offset,
           &isnull, &isout);

 if (isout)
 {
  /*
   * target row is out of the partition; supply default value if
   * provided.  otherwise it'll stay NULL
 */

  if (withdefault)
   result = WinGetFuncArgCurrent(winobj, 2, &isnull);
 }

 if (isnull)
  PG_RETURN_NULL();

 PG_RETURN_DATUM(result);
}

/*
 * lag
 * returns the value of VE evaluated on a row that is 1
 * row before the current row within a partition,
 * per spec.
 */

Datum
window_lag(PG_FUNCTION_ARGS)
{
 return leadlag_common(fcinfo, false, false, false);
}

/*
 * lag_with_offset
 * returns the value of VE evaluated on a row that is OFFSET
 * rows before the current row within a partition,
 * per spec.
 */

Datum
window_lag_with_offset(PG_FUNCTION_ARGS)
{
 return leadlag_common(fcinfo, false, true, false);
}

/*
 * lag_with_offset_and_default
 * same as lag_with_offset but accepts default value
 * as its third argument.
 */

Datum
window_lag_with_offset_and_default(PG_FUNCTION_ARGS)
{
 return leadlag_common(fcinfo, false, true, true);
}

/*
 * lead
 * returns the value of VE evaluated on a row that is 1
 * row after the current row within a partition,
 * per spec.
 */

Datum
window_lead(PG_FUNCTION_ARGS)
{
 return leadlag_common(fcinfo, true, false, false);
}

/*
 * lead_with_offset
 * returns the value of VE evaluated on a row that is OFFSET
 * number of rows after the current row within a partition,
 * per spec.
 */

Datum
window_lead_with_offset(PG_FUNCTION_ARGS)
{
 return leadlag_common(fcinfo, true, true, false);
}

/*
 * lead_with_offset_and_default
 * same as lead_with_offset but accepts default value
 * as its third argument.
 */

Datum
window_lead_with_offset_and_default(PG_FUNCTION_ARGS)
{
 return leadlag_common(fcinfo, true, true, true);
}

/*
 * first_value
 * return the value of VE evaluated on the first row of the
 * window frame, per spec.
 */

Datum
window_first_value(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 Datum  result;
 bool  isnull;

 result = WinGetFuncArgInFrame(winobj, 0,
          0, WINDOW_SEEK_HEAD, true,
          &isnull, NULL);
 if (isnull)
  PG_RETURN_NULL();

 PG_RETURN_DATUM(result);
}

/*
 * last_value
 * return the value of VE evaluated on the last row of the
 * window frame, per spec.
 */

Datum
window_last_value(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 Datum  result;
 bool  isnull;

 result = WinGetFuncArgInFrame(winobj, 0,
          0, WINDOW_SEEK_TAIL, true,
          &isnull, NULL);
 if (isnull)
  PG_RETURN_NULL();

 PG_RETURN_DATUM(result);
}

/*
 * nth_value
 * return the value of VE evaluated on the n-th row from the first
 * row of the window frame, per spec.
 */

Datum
window_nth_value(PG_FUNCTION_ARGS)
{
 WindowObject winobj = PG_WINDOW_OBJECT();
 bool  const_offset;
 Datum  result;
 bool  isnull;
 int32  nth;

 nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
 if (isnull)
  PG_RETURN_NULL();
 const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);

 if (nth <= 0)
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE),
     errmsg("argument of nth_value must be greater than zero")));

 result = WinGetFuncArgInFrame(winobj, 0,
          nth - 1, WINDOW_SEEK_HEAD, const_offset,
          &isnull, NULL);
 if (isnull)
  PG_RETURN_NULL();

 PG_RETURN_DATUM(result);
}

Messung V0.5 in Prozent
C=93 H=92 G=92

[Dauer der Verarbeitung: 0.16 Sekunden, vorverarbeitet 2026-10-11]