Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  queryjumblefuncs.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * queryjumblefuncs.c
 *  Query normalization and fingerprinting.
 *
 * Normalization is a process whereby similar queries, typically differing only
 * in their constants (though the exact rules are somewhat more subtle than
 * that) are recognized as equivalent, and are tracked as a single entry.  This
 * is particularly useful for non-prepared queries.
 *
 * Normalization is implemented by fingerprinting queries, selectively
 * serializing those fields of each query tree's nodes that are judged to be
 * essential to the query.  This is referred to as a query jumble.  This is
 * distinct from a regular serialization in that various extraneous
 * information is ignored as irrelevant or not essential to the query, such
 * as the collations of Vars and, most notably, the values of constants.
 *
 * This jumble is acquired at the end of parse analysis of each query, and
 * a 64-bit hash of it is stored into the query's Query.queryId field.
 * The server then copies this value around, making it available in plan
 * tree(s) generated from the query.  The executor can then use this value
 * to blame query costs on the proper queryId.
 *
 * Arrays of two or more constants and PARAM_EXTERN parameters are "squashed"
 * and contribute only once to the jumble.  This has the effect that queries
 * that differ only on the length of such lists have the same queryId.
 *
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *   src/backend/nodes/queryjumblefuncs.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/transam.h"
#include "catalog/pg_proc.h"
#include "common/hashfn.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "nodes/queryjumble.h"
#include "utils/lsyscache.h"
#include "parser/scansup.h"

#define JUMBLE_SIZE    1024 /* query serialization buffer size */

/* GUC parameters */
int   compute_query_id = COMPUTE_QUERY_ID_AUTO;

/*
 * True when compute_query_id is ON or AUTO, and a module requests them.
 *
 * Note that IsQueryIdEnabled() should be used instead of checking
 * query_id_enabled or compute_query_id directly when we want to know
 * whether query identifiers are computed in the core or not.
 */

bool  query_id_enabled = false;

static JumbleState *InitJumble(void);
static int64 DoJumble(JumbleState *jstate, Node *node);
static void AppendJumble(JumbleState *jstate,
       const unsigned char *value, Size size);
static void FlushPendingNulls(JumbleState *jstate);
static void RecordConstLocation(JumbleState *jstate,
        bool extern_param,
        int location, int len);
static void _jumbleNode(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
static void _jumbleElements(JumbleState *jstate, List *elements, Node *node);
static void _jumbleParam(JumbleState *jstate, Node *node);
static void _jumbleA_Const(JumbleState *jstate, Node *node);
static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node);
static void _jumbleRangeTblEntry_eref(JumbleState *jstate,
           RangeTblEntry *rte,
           Alias *expr);

/*
 * Given a possibly multi-statement source string, confine our attention to the
 * relevant part of the string.
 */

const char *
CleanQuerytext(const char *query, int *location, int *len)
{
 int   query_location = *location;
 int   query_len = *len;

 /* First apply starting offset, unless it's -1 (unknown). */
 if (query_location >= 0)
 {
  Assert(query_location <= strlen(query));
  query += query_location;
  /* Length of 0 (or -1) means "rest of string" */
  if (query_len <= 0)
   query_len = strlen(query);
  else
   Assert(query_len <= strlen(query));
 }
 else
 {
  /* If query location is unknown, distrust query_len as well */
  query_location = 0;
  query_len = strlen(query);
 }

 /*
  * Discard leading and trailing whitespace, too.  Use scanner_isspace()
  * not libc's isspace(), because we want to match the lexer's behavior.
  *
  * Note: the parser now strips leading comments and whitespace from the
  * reported stmt_location, so this first loop will only iterate in the
  * unusual case that the location didn't propagate to here.  But the
  * statement length will extend to the end-of-string or terminating
  * semicolon, so the second loop often does something useful.
 */

 while (query_len > 0 && scanner_isspace(query[0]))
  query++, query_location++, query_len--;
 while (query_len > 0 && scanner_isspace(query[query_len - 1]))
  query_len--;

 *location = query_location;
 *len = query_len;

 return query;
}

/*
 * JumbleQuery
 *  Recursively process the given Query producing a 64-bit hash value by
 *  hashing the relevant fields and record that value in the Query's queryId
 *  field.  Return the JumbleState object used for jumbling the query.
 */

JumbleState *
JumbleQuery(Query *query)
{
 JumbleState *jstate;

 Assert(IsQueryIdEnabled());

 jstate = InitJumble();

 query->queryId = DoJumble(jstate, (Node *) query);

 /*
  * If we are unlucky enough to get a hash of zero, use 1 instead for
  * normal statements and 2 for utility queries.
 */

 if (query->queryId == INT64CONST(0))
 {
  if (query->utilityStmt)
   query->queryId = INT64CONST(2);
  else
   query->queryId = INT64CONST(1);
 }

 return jstate;
}

/*
 * Enables query identifier computation.
 *
 * Third-party plugins can use this function to inform core that they require
 * a query identifier to be computed.
 */

void
EnableQueryId(void)
{
 if (compute_query_id != COMPUTE_QUERY_ID_OFF)
  query_id_enabled = true;
}

/*
 * InitJumble
 *  Allocate a JumbleState object and make it ready to jumble.
 */

static JumbleState *
InitJumble(void)
{
 JumbleState *jstate;

 jstate = (JumbleState *) palloc(sizeof(JumbleState));

 /* Set up workspace for query jumbling */
 jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
 jstate->jumble_len = 0;
 jstate->clocations_buf_size = 32;
 jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size *
            sizeof(LocationLen));
 jstate->clocations_count = 0;
 jstate->highest_extern_param_id = 0;
 jstate->pending_nulls = 0;
 jstate->has_squashed_lists = false;
#ifdef USE_ASSERT_CHECKING
 jstate->total_jumble_len = 0;
#endif

 return jstate;
}

/*
 * DoJumble
 *  Jumble the given Node using the given JumbleState and return the resulting
 *  jumble hash.
 */

static int64
DoJumble(JumbleState *jstate, Node *node)
{
 /* Jumble the given node */
 _jumbleNode(jstate, node);

 /* Flush any pending NULLs before doing the final hash */
 if (jstate->pending_nulls > 0)
  FlushPendingNulls(jstate);

 /* Squashed list found, reset highest_extern_param_id */
 if (jstate->has_squashed_lists)
  jstate->highest_extern_param_id = 0;

 /* Process the jumble buffer and produce the hash value */
 return DatumGetInt64(hash_any_extended(jstate->jumble,
             jstate->jumble_len,
             0));
}

/*
 * AppendJumbleInternal: Internal function for appending to the jumble buffer
 *
 * Note: Callers must ensure that size > 0.
 */

static pg_attribute_always_inline void
AppendJumbleInternal(JumbleState *jstate, const unsigned char *item,
      Size size)
{
 unsigned char *jumble = jstate->jumble;
 Size  jumble_len = jstate->jumble_len;

 /* Ensure the caller didn't mess up */
 Assert(size > 0);

 /*
  * Fast path for when there's enough space left in the buffer.  This is
  * worthwhile as means the memcpy can be inlined into very efficient code
  * when 'size' is a compile-time constant.
 */

 if (likely(size <= JUMBLE_SIZE - jumble_len))
 {
  memcpy(jumble + jumble_len, item, size);
  jstate->jumble_len += size;

#ifdef USE_ASSERT_CHECKING
  jstate->total_jumble_len += size;
#endif

  return;
 }

 /*
  * Whenever the jumble buffer is full, we hash the current contents and
  * reset the buffer to contain just that hash value, thus relying on the
  * hash to summarize everything so far.
 */

 do
 {
  Size  part_size;

  if (unlikely(jumble_len >= JUMBLE_SIZE))
  {
   int64  start_hash;

   start_hash = DatumGetInt64(hash_any_extended(jumble,
               JUMBLE_SIZE, 0));
   memcpy(jumble, &start_hash, sizeof(start_hash));
   jumble_len = sizeof(start_hash);
  }
  part_size = Min(size, JUMBLE_SIZE - jumble_len);
  memcpy(jumble + jumble_len, item, part_size);
  jumble_len += part_size;
  item += part_size;
  size -= part_size;

#ifdef USE_ASSERT_CHECKING
  jstate->total_jumble_len += part_size;
#endif
 } while (size > 0);

 jstate->jumble_len = jumble_len;
}

/*
 * AppendJumble
 *  Add 'size' bytes of the given jumble 'value' to the jumble state
 */

static pg_noinline void
AppendJumble(JumbleState *jstate, const unsigned char *value, Size size)
{
 if (jstate->pending_nulls > 0)
  FlushPendingNulls(jstate);

 AppendJumbleInternal(jstate, value, size);
}

/*
 * AppendJumbleNull
 *  For jumbling NULL pointers
 */

static pg_attribute_always_inline void
AppendJumbleNull(JumbleState *jstate)
{
 jstate->pending_nulls++;
}

/*
 * AppendJumble8
 *  Add the first byte from the given 'value' pointer to the jumble state
 */

static pg_noinline void
AppendJumble8(JumbleState *jstate, const unsigned char *value)
{
 if (jstate->pending_nulls > 0)
  FlushPendingNulls(jstate);

 AppendJumbleInternal(jstate, value, 1);
}

/*
 * AppendJumble16
 *  Add the first 2 bytes from the given 'value' pointer to the jumble
 *  state.
 */

static pg_noinline void
AppendJumble16(JumbleState *jstate, const unsigned char *value)
{
 if (jstate->pending_nulls > 0)
  FlushPendingNulls(jstate);

 AppendJumbleInternal(jstate, value, 2);
}

/*
 * AppendJumble32
 *  Add the first 4 bytes from the given 'value' pointer to the jumble
 *  state.
 */

static pg_noinline void
AppendJumble32(JumbleState *jstate, const unsigned char *value)
{
 if (jstate->pending_nulls > 0)
  FlushPendingNulls(jstate);

 AppendJumbleInternal(jstate, value, 4);
}

/*
 * AppendJumble64
 *  Add the first 8 bytes from the given 'value' pointer to the jumble
 *  state.
 */

static pg_noinline void
AppendJumble64(JumbleState *jstate, const unsigned char *value)
{
 if (jstate->pending_nulls > 0)
  FlushPendingNulls(jstate);

 AppendJumbleInternal(jstate, value, 8);
}

/*
 * FlushPendingNulls
 *  Incorporate the pending_nulls value into the jumble buffer.
 *
 * Note: Callers must ensure that there's at least 1 pending NULL.
 */

static pg_attribute_always_inline void
FlushPendingNulls(JumbleState *jstate)
{
 Assert(jstate->pending_nulls > 0);

 AppendJumbleInternal(jstate,
       (const unsigned char *) &jstate->pending_nulls, 4);
 jstate->pending_nulls = 0;
}


/*
 * Record the location of some kind of constant within a query string.
 * These are not only bare constants but also expressions that ultimately
 * constitute a constant, such as those inside casts and simple function
 * calls; if extern_param, then it corresponds to a PARAM_EXTERN Param.
 *
 * If length is -1, it indicates a single such constant element.  If
 * it's a positive integer, it indicates the length of a squashable
 * list of them.
 */

static void
RecordConstLocation(JumbleState *jstate, bool extern_param, int location, int len)
{
 /* -1 indicates unknown or undefined location */
 if (location >= 0)
 {
  /* enlarge array if needed */
  if (jstate->clocations_count >= jstate->clocations_buf_size)
  {
   jstate->clocations_buf_size *= 2;
   jstate->clocations = (LocationLen *)
    repalloc(jstate->clocations,
       jstate->clocations_buf_size *
       sizeof(LocationLen));
  }
  jstate->clocations[jstate->clocations_count].location = location;

  /*
   * Lengths are either positive integers (indicating a squashable
   * list), or -1.
 */

  Assert(len > -1 || len == -1);
  jstate->clocations[jstate->clocations_count].length = len;
  jstate->clocations[jstate->clocations_count].squashed = (len > -1);
  jstate->clocations[jstate->clocations_count].extern_param = extern_param;
  jstate->clocations_count++;
 }
}

/*
 * Subroutine for _jumbleElements: Verify a few simple cases where we can
 * deduce that the expression is a constant:
 *
 * - See through any wrapping RelabelType and CoerceViaIO layers.
 * - If it's a FuncExpr, check that the function is a builtin
 *   cast and its arguments are Const.
 * - Otherwise test if the expression is a simple Const or a
 *   PARAM_EXTERN param.
 */

static bool
IsSquashableConstant(Node *element)
{
restart:
 switch (nodeTag(element))
 {
  case T_RelabelType:
   /* Unwrap RelabelType */
   element = (Node *) ((RelabelType *) element)->arg;
   goto restart;

  case T_CoerceViaIO:
   /* Unwrap CoerceViaIO */
   element = (Node *) ((CoerceViaIO *) element)->arg;
   goto restart;

  case T_Const:
   return true;

  case T_Param:
   return castNode(Param, element)->paramkind == PARAM_EXTERN;

  case T_FuncExpr:
   {
    FuncExpr   *func = (FuncExpr *) element;
    ListCell   *temp;

    if (func->funcformat != COERCE_IMPLICIT_CAST &&
     func->funcformat != COERCE_EXPLICIT_CAST)
     return false;

    if (func->funcid > FirstGenbkiObjectId)
     return false;

    /*
     * We can check function arguments recursively, being careful
     * about recursing too deep.  At each recursion level it's
     * enough to test the stack on the first element.  (Note that
     * I wasn't able to hit this without bloating the stack
     * artificially in this function: the parser errors out before
     * stack size becomes a problem here.)
 */

    foreach(temp, func->args)
    {
     Node    *arg = lfirst(temp);

     if (!IsA(arg, Const))
     {
      if (foreach_current_index(temp) == 0 &&
       stack_is_too_deep())
       return false;
      else if (!IsSquashableConstant(arg))
       return false;
     }
    }

    return true;
   }

  default:
   return false;
 }
}

/*
 * Subroutine for _jumbleElements: Verify whether the provided list
 * can be squashed, meaning it contains only constant expressions.
 *
 * Return value indicates if squashing is possible.
 *
 * Note that this function searches only for explicit Const nodes with
 * possibly very simple decorations on top and PARAM_EXTERN parameters,
 * and does not try to simplify expressions.
 */

static bool
IsSquashableConstantList(List *elements)
{
 ListCell   *temp;

 /* If the list is too short, we don't try to squash it. */
 if (list_length(elements) < 2)
  return false;

 foreach(temp, elements)
 {
  if (!IsSquashableConstant(lfirst(temp)))
   return false;
 }

 return true;
}

#define JUMBLE_NODE(item) \
 _jumbleNode(jstate, (Node *) expr->item)
#define JUMBLE_ELEMENTS(list, node) \
 _jumbleElements(jstate, (List *) expr->list, node)
#define JUMBLE_LOCATION(location) \
 RecordConstLocation(jstate, false, expr->location, -1)
#define JUMBLE_FIELD(item) \
do { \
 if (sizeof(expr->item) == 8) \
  AppendJumble64(jstate, (const unsigned char *) &(expr->item)); \
 else if (sizeof(expr->item) == 4) \
  AppendJumble32(jstate, (const unsigned char *) &(expr->item)); \
 else if (sizeof(expr->item) == 2) \
  AppendJumble16(jstate, (const unsigned char *) &(expr->item)); \
 else if (sizeof(expr->item) == 1) \
  AppendJumble8(jstate, (const unsigned char *) &(expr->item)); \
 else \
  AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)); \
while (0)
#define JUMBLE_STRING(str) \
do { \
 if (expr->str) \
  AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
 else \
  AppendJumbleNull(jstate); \
while(0)
/* Function name used for the node field attribute custom_query_jumble. */
#define JUMBLE_CUSTOM(nodetype, item) \
 _jumble##nodetype##_##item(jstate, expr, expr->item)

#include "queryjumblefuncs.funcs.c"

static void
_jumbleNode(JumbleState *jstate, Node *node)
{
 Node    *expr = node;
#ifdef USE_ASSERT_CHECKING
 Size  prev_jumble_len = jstate->total_jumble_len;
#endif

 if (expr == NULL)
 {
  AppendJumbleNull(jstate);
  return;
 }

 /* Guard against stack overflow due to overly complex expressions */
 check_stack_depth();

 /*
  * We always emit the node's NodeTag, then any additional fields that are
  * considered significant, and then we recurse to any child nodes.
 */

 JUMBLE_FIELD(type);

 switch (nodeTag(expr))
 {
#include "queryjumblefuncs.switch.c"

  case T_List:
  case T_IntList:
  case T_OidList:
  case T_XidList:
   _jumbleList(jstate, expr);
   break;

  default:
   /* Only a warning, since we can stumble along anyway */
   elog(WARNING, "unrecognized node type: %d",
     (int) nodeTag(expr));
   break;
 }

 /* Ensure we added something to the jumble buffer */
 Assert(jstate->total_jumble_len > prev_jumble_len);
}

static void
_jumbleList(JumbleState *jstate, Node *node)
{
 List    *expr = (List *) node;
 ListCell   *l;

 switch (expr->type)
 {
  case T_List:
   foreach(l, expr)
    _jumbleNode(jstate, lfirst(l));
   break;
  case T_IntList:
   foreach(l, expr)
    AppendJumble32(jstate, (const unsigned char *) &lfirst_int(l));
   break;
  case T_OidList:
   foreach(l, expr)
    AppendJumble32(jstate, (const unsigned char *) &lfirst_oid(l));
   break;
  case T_XidList:
   foreach(l, expr)
    AppendJumble32(jstate, (const unsigned char *) &lfirst_xid(l));
   break;
  default:
   elog(ERROR, "unrecognized list node type: %d",
     (int) expr->type);
   return;
 }
}

/*
 * We try to jumble lists of expressions as one individual item regardless
 * of how many elements are in the list. This is know as squashing, which
 * results in different queries jumbling to the same query_id, if the only
 * difference is the number of elements in the list.
 *
 * We allow constants and PARAM_EXTERN parameters to be squashed. To normalize
 * such queries, we use the start and end locations of the list of elements in
 * a list.
 */

static void
_jumbleElements(JumbleState *jstate, List *elements, Node *node)
{
 bool  normalize_list = false;

 if (IsSquashableConstantList(elements))
 {
  if (IsA(node, ArrayExpr))
  {
   ArrayExpr  *aexpr = (ArrayExpr *) node;

   if (aexpr->list_start > 0 && aexpr->list_end > 0)
   {
    RecordConstLocation(jstate,
         false,
         aexpr->list_start + 1,
         (aexpr->list_end - aexpr->list_start) - 1);
    normalize_list = true;
    jstate->has_squashed_lists = true;
   }
  }
 }

 if (!normalize_list)
 {
  _jumbleNode(jstate, (Node *) elements);
 }
}

/*
 * We store the highest param ID of extern params.  This can later be used
 * to start the numbering of the placeholder for squashed lists.
 */

static void
_jumbleParam(JumbleState *jstate, Node *node)
{
 Param    *expr = (Param *) node;

 JUMBLE_FIELD(paramkind);
 JUMBLE_FIELD(paramid);
 JUMBLE_FIELD(paramtype);
 /* paramtypmode and paramcollid are ignored */

 if (expr->paramkind == PARAM_EXTERN)
 {
  /*
   * At this point, only external parameter locations outside of
   * squashable lists will be recorded.
 */

  RecordConstLocation(jstate, true, expr->location, -1);

  /*
   * Update the highest Param id seen, in order to start normalization
   * correctly.
   *
   * Note: This value is reset at the end of jumbling if there exists a
   * squashable list. See the comment in the definition of JumbleState.
 */

  if (expr->paramid > jstate->highest_extern_param_id)
   jstate->highest_extern_param_id = expr->paramid;
 }
}

static void
_jumbleA_Const(JumbleState *jstate, Node *node)
{
 A_Const    *expr = (A_Const *) node;

 JUMBLE_FIELD(isnull);
 if (!expr->isnull)
 {
  JUMBLE_FIELD(val.node.type);
  switch (nodeTag(&expr->val))
  {
   case T_Integer:
    JUMBLE_FIELD(val.ival.ival);
    break;
   case T_Float:
    JUMBLE_STRING(val.fval.fval);
    break;
   case T_Boolean:
    JUMBLE_FIELD(val.boolval.boolval);
    break;
   case T_String:
    JUMBLE_STRING(val.sval.sval);
    break;
   case T_BitString:
    JUMBLE_STRING(val.bsval.bsval);
    break;
   default:
    elog(ERROR, "unrecognized node type: %d",
      (int) nodeTag(&expr->val));
    break;
  }
 }
}

static void
_jumbleVariableSetStmt(JumbleState *jstate, Node *node)
{
 VariableSetStmt *expr = (VariableSetStmt *) node;

 JUMBLE_FIELD(kind);
 JUMBLE_STRING(name);

 /*
  * Account for the list of arguments in query jumbling only if told by the
  * parser.
 */

 if (expr->jumble_args)
  JUMBLE_NODE(args);
 JUMBLE_FIELD(is_local);
 JUMBLE_LOCATION(location);
}

/*
 * Custom query jumble function for RangeTblEntry.eref.
 */

static void
_jumbleRangeTblEntry_eref(JumbleState *jstate,
        RangeTblEntry *rte,
        Alias *expr)
{
 JUMBLE_FIELD(type);

 /*
  * This includes only the table name, the list of column names is ignored.
 */

 JUMBLE_STRING(aliasname);
}

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

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002