PostgreSQL Source Code git master
Loading...
Searching...
No Matches
queryjumblefuncs.c File Reference
#include "postgres.h"
#include "access/transam.h"
#include "catalog/pg_proc.h"
#include "common/hashfn.h"
#include "common/int.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "nodes/queryjumble.h"
#include "utils/lsyscache.h"
#include "parser/scanner.h"
#include "parser/scansup.h"
#include "queryjumblefuncs.funcs.c"
#include "queryjumblefuncs.switch.c"
Include dependency graph for queryjumblefuncs.c:

Go to the source code of this file.

Macros

#define JUMBLE_SIZE   1024 /* query serialization buffer size */
 
#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)
 
#define JUMBLE_STRING(str)
 
#define JUMBLE_CUSTOM(nodetype, item)    _jumble##nodetype##_##item(jstate, expr, expr->item)
 

Functions

static JumbleStateInitJumble (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)
 
const charCleanQuerytext (const char *query, int *location, int *len)
 
JumbleStateJumbleQuery (Query *query)
 
void EnableQueryId (void)
 
static pg_attribute_always_inline void AppendJumbleInternal (JumbleState *jstate, const unsigned char *item, Size size)
 
static pg_attribute_always_inline void AppendJumbleNull (JumbleState *jstate)
 
static pg_noinline void AppendJumble8 (JumbleState *jstate, const unsigned char *value)
 
static pg_noinline void AppendJumble16 (JumbleState *jstate, const unsigned char *value)
 
static pg_noinline void AppendJumble32 (JumbleState *jstate, const unsigned char *value)
 
static pg_noinline void AppendJumble64 (JumbleState *jstate, const unsigned char *value)
 
static bool IsSquashableConstant (Node *element)
 
static bool IsSquashableConstantList (List *elements)
 
static int CompLocation (const void *a, const void *b)
 
LocationLenComputeConstantLengths (const JumbleState *jstate, const char *query, int query_loc)
 

Variables

int compute_query_id = COMPUTE_QUERY_ID_AUTO
 
bool query_id_enabled = false
 

Macro Definition Documentation

◆ JUMBLE_CUSTOM

#define JUMBLE_CUSTOM (   nodetype,
  item 
)     _jumble##nodetype##_##item(jstate, expr, expr->item)

Definition at line 557 of file queryjumblefuncs.c.

563{
564 Node *expr = node;
565#ifdef USE_ASSERT_CHECKING
566 Size prev_jumble_len = jstate->total_jumble_len;
567#endif
568
569 if (expr == NULL)
570 {
572 return;
573 }
574
575 /* Guard against stack overflow due to overly complex expressions */
577
578 /*
579 * We always emit the node's NodeTag, then any additional fields that are
580 * considered significant, and then we recurse to any child nodes.
581 */
583
584 switch (nodeTag(expr))
585 {
586#include "queryjumblefuncs.switch.c"
587
588 case T_List:
589 case T_IntList:
590 case T_OidList:
591 case T_XidList:
592 _jumbleList(jstate, expr);
593 break;
594
595 default:
596 /* Only a warning, since we can stumble along anyway */
597 elog(WARNING, "unrecognized node type: %d",
598 (int) nodeTag(expr));
599 break;
600 }
601
602 /* Ensure we added something to the jumble buffer */
603 Assert(jstate->total_jumble_len > prev_jumble_len);
604}
605
606static void
608{
609 List *expr = (List *) node;
610 ListCell *l;
611
612 switch (expr->type)
613 {
614 case T_List:
615 foreach(l, expr)
617 break;
618 case T_IntList:
619 foreach(l, expr)
620 AppendJumble32(jstate, (const unsigned char *) &lfirst_int(l));
621 break;
622 case T_OidList:
623 foreach(l, expr)
624 AppendJumble32(jstate, (const unsigned char *) &lfirst_oid(l));
625 break;
626 case T_XidList:
627 foreach(l, expr)
628 AppendJumble32(jstate, (const unsigned char *) &lfirst_xid(l));
629 break;
630 default:
631 elog(ERROR, "unrecognized list node type: %d",
632 (int) expr->type);
633 return;
634 }
635}
636
637/*
638 * We try to jumble lists of expressions as one individual item regardless
639 * of how many elements are in the list. This is know as squashing, which
640 * results in different queries jumbling to the same query_id, if the only
641 * difference is the number of elements in the list.
642 *
643 * We allow constants and PARAM_EXTERN parameters to be squashed. To normalize
644 * such queries, we use the start and end locations of the list of elements in
645 * a list.
646 */
647static void
648_jumbleElements(JumbleState *jstate, List *elements, Node *node)
649{
650 bool normalize_list = false;
651
652 if (IsSquashableConstantList(elements))
653 {
654 if (IsA(node, ArrayExpr))
655 {
656 ArrayExpr *aexpr = (ArrayExpr *) node;
657
658 if (aexpr->list_start > 0 && aexpr->list_end > 0)
659 {
661 false,
662 aexpr->list_start + 1,
663 (aexpr->list_end - aexpr->list_start) - 1);
664 normalize_list = true;
665 jstate->has_squashed_lists = true;
666 }
667 }
668 }
669
670 if (!normalize_list)
671 {
672 _jumbleNode(jstate, (Node *) elements);
673 }
674}
675
676/*
677 * We store the highest param ID of extern params. This can later be used
678 * to start the numbering of the placeholder for squashed lists.
679 */
680static void
682{
683 Param *expr = (Param *) node;
684
685 JUMBLE_FIELD(paramkind);
686 JUMBLE_FIELD(paramid);
687 JUMBLE_FIELD(paramtype);
688 /* paramtypmod and paramcollid are ignored */
689
690 if (expr->paramkind == PARAM_EXTERN)
691 {
692 /*
693 * At this point, only external parameter locations outside of
694 * squashable lists will be recorded.
695 */
696 RecordConstLocation(jstate, true, expr->location, -1);
697
698 /*
699 * Update the highest Param id seen, in order to start normalization
700 * correctly.
701 *
702 * Note: This value is reset at the end of jumbling if there exists a
703 * squashable list. See the comment in the definition of JumbleState.
704 */
705 if (expr->paramid > jstate->highest_extern_param_id)
706 jstate->highest_extern_param_id = expr->paramid;
707 }
708}
709
710static void
712{
713 A_Const *expr = (A_Const *) node;
714
715 JUMBLE_FIELD(isnull);
716 if (!expr->isnull)
717 {
718 JUMBLE_FIELD(val.node.type);
719 switch (nodeTag(&expr->val))
720 {
721 case T_Integer:
722 JUMBLE_FIELD(val.ival.ival);
723 break;
724 case T_Float:
725 JUMBLE_STRING(val.fval.fval);
726 break;
727 case T_Boolean:
728 JUMBLE_FIELD(val.boolval.boolval);
729 break;
730 case T_String:
731 JUMBLE_STRING(val.sval.sval);
732 break;
733 case T_BitString:
734 JUMBLE_STRING(val.bsval.bsval);
735 break;
736 default:
737 elog(ERROR, "unrecognized node type: %d",
738 (int) nodeTag(&expr->val));
739 break;
740 }
741 }
742}
743
744static void
746{
747 VariableSetStmt *expr = (VariableSetStmt *) node;
748
749 JUMBLE_FIELD(kind);
751
752 /*
753 * Account for the list of arguments in query jumbling only if told by the
754 * parser.
755 */
756 if (expr->jumble_args)
757 JUMBLE_NODE(args);
758 JUMBLE_FIELD(is_local);
759 JUMBLE_LOCATION(location);
760}
761
762/*
763 * Custom query jumble function for RangeTblEntry.eref.
764 */
765static void
768 Alias *expr)
769{
771
772 /*
773 * This includes only the table name, the list of column names is ignored.
774 */
775 JUMBLE_STRING(aliasname);
776}
777
778/*
779 * CompLocation: comparator for qsorting LocationLen structs by location
780 */
781static int
782CompLocation(const void *a, const void *b)
783{
784 int l = ((const LocationLen *) a)->location;
785 int r = ((const LocationLen *) b)->location;
786
787 return pg_cmp_s32(l, r);
788}
789
790/*
791 * Given a valid SQL string and an array of constant-location records, return
792 * the textual lengths of those constants in a newly allocated LocationLen
793 * array, or NULL if there are no constants.
794 *
795 * The constants may use any allowed constant syntax, such as float literals,
796 * bit-strings, single-quoted strings and dollar-quoted strings. This is
797 * accomplished by using the public API for the core scanner.
798 *
799 * It is the caller's job to ensure that the string is a valid SQL statement
800 * with constants at the indicated locations. Since in practice the string
801 * has already been parsed, and the locations that the caller provides will
802 * have originated from within the authoritative parser, this should not be
803 * a problem.
804 *
805 * Multiple constants can have the same location. We reset lengths of those
806 * past the first to -1 so that they can later be ignored.
807 *
808 * If query_loc > 0, then "query" has been advanced by that much compared to
809 * the original string start, as is the case with multi-statement strings, so
810 * we need to translate the provided locations to compensate. (This lets us
811 * avoid re-scanning statements before the one of interest, so it's worth
812 * doing.)
813 *
814 * N.B. There is an assumption that a '-' character at a Const location begins
815 * a negative numeric constant. This precludes there ever being another
816 * reason for a constant to start with a '-'.
817 *
818 * It is the caller's responsibility to free the result, if necessary.
819 */
821ComputeConstantLengths(const JumbleState *jstate, const char *query,
822 int query_loc)
823{
825 core_yyscan_t yyscanner;
829
830 if (jstate->clocations_count == 0)
831 return NULL;
832
833 /* Copy constant locations to avoid modifying jstate */
834 locs = palloc_array(LocationLen, jstate->clocations_count);
835 memcpy(locs, jstate->clocations, jstate->clocations_count * sizeof(LocationLen));
836
837 /*
838 * Sort the records by location so that we can process them in order while
839 * scanning the query text.
840 */
841 if (jstate->clocations_count > 1)
842 qsort(locs, jstate->clocations_count,
843 sizeof(LocationLen), CompLocation);
844
845 /* initialize the flex scanner --- should match raw_parser() */
846 yyscanner = scanner_init(query,
847 &yyextra,
850
851 /* Search for each constant, in sequence */
852 for (int i = 0; i < jstate->clocations_count; i++)
853 {
854 int loc;
855 int tok;
856
857 /* Ignore constants after the first one in the same location */
858 if (i > 0 && locs[i].location == locs[i - 1].location)
859 {
860 locs[i].length = -1;
861 continue;
862 }
863
864 if (locs[i].squashed)
865 continue; /* squashable list, ignore */
866
867 /*
868 * Adjust the constant's location using the provided starting location
869 * of the current statement. This allows us to avoid scanning a
870 * multi-statement string from the beginning.
871 */
872 loc = locs[i].location - query_loc;
873 Assert(loc >= 0);
874
875 /*
876 * We have a valid location for a constant that's not a dupe. Lex
877 * tokens until we find the desired constant.
878 */
879 for (;;)
880 {
881 tok = core_yylex(&yylval, &yylloc, yyscanner);
882
883 /* We should not hit end-of-string, but if we do, behave sanely */
884 if (tok == 0)
885 break; /* out of inner for-loop */
886
887 /*
888 * We should find the token position exactly, but if we somehow
889 * run past it, work with that.
890 */
891 if (yylloc >= loc)
892 {
893 if (query[loc] == '-')
894 {
895 /*
896 * It's a negative value - this is the one and only case
897 * where we replace more than a single token.
898 *
899 * Do not compensate for the special-case adjustment of
900 * location to that of the leading '-' operator in the
901 * event of a negative constant (see doNegate() in
902 * gram.y). It is also useful for our purposes to start
903 * from the minus symbol. In this way, queries like
904 * "select * from foo where bar = 1" and "select * from
905 * foo where bar = -2" can be treated similarly.
906 */
907 tok = core_yylex(&yylval, &yylloc, yyscanner);
908 if (tok == 0)
909 break; /* out of inner for-loop */
910 }
911
912 /*
913 * We now rely on the assumption that flex has placed a zero
914 * byte after the text of the current token in scanbuf.
915 */
916 locs[i].length = strlen(yyextra.scanbuf + loc);
917 break; /* out of inner for-loop */
918 }
919 }
920
921 /* If we hit end-of-string, give up, leaving remaining lengths -1 */
922 if (tok == 0)
923 break;
924 }
925
926 scanner_finish(yyscanner);
927
928 return locs;
929}
#define Assert(condition)
Definition c.h:943
size_t Size
Definition c.h:689
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
#define WARNING
Definition elog.h:37
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define palloc_array(type, count)
Definition fe_memutils.h:76
long val
Definition informix.c:689
static int pg_cmp_s32(int32 a, int32 b)
Definition int.h:713
int b
Definition isn.c:74
int a
Definition isn.c:73
int i
Definition isn.c:77
PGDLLIMPORT const ScanKeywordList ScanKeywords
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define nodeTag(nodeptr)
Definition nodes.h:139
#define lfirst(lc)
Definition pg_list.h:172
#define lfirst_int(lc)
Definition pg_list.h:173
#define lfirst_oid(lc)
Definition pg_list.h:174
#define lfirst_xid(lc)
Definition pg_list.h:175
#define qsort(a, b, c, d)
Definition port.h:495
static int fb(int x)
const char * YYLTYPE
@ PARAM_EXTERN
Definition primnodes.h:385
#define JUMBLE_NODE(item)
static void _jumbleNode(JumbleState *jstate, Node *node)
static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node)
static bool IsSquashableConstantList(List *elements)
static int CompLocation(const void *a, const void *b)
static pg_attribute_always_inline void AppendJumbleNull(JumbleState *jstate)
#define JUMBLE_LOCATION(location)
static void _jumbleParam(JumbleState *jstate, Node *node)
static void _jumbleList(JumbleState *jstate, Node *node)
static void RecordConstLocation(JumbleState *jstate, bool extern_param, int location, int len)
#define JUMBLE_STRING(str)
static pg_noinline void AppendJumble32(JumbleState *jstate, const unsigned char *value)
static void _jumbleA_Const(JumbleState *jstate, Node *node)
static void _jumbleElements(JumbleState *jstate, List *elements, Node *node)
LocationLen * ComputeConstantLengths(const JumbleState *jstate, const char *query, int query_loc)
static void _jumbleRangeTblEntry_eref(JumbleState *jstate, RangeTblEntry *rte, Alias *expr)
#define JUMBLE_FIELD(item)
core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, const ScanKeywordList *keywordlist, const uint16 *keyword_tokens)
Definition scan.l:1233
#define yylloc
Definition scan.l:1106
void scanner_finish(core_yyscan_t yyscanner)
Definition scan.l:1273
#define yyextra
Definition scan.l:1102
const uint16 ScanKeywordTokens[]
Definition scan.l:80
void * core_yyscan_t
Definition scanner.h:118
int core_yylex(core_YYSTYPE *yylval_param, YYLTYPE *yylloc_param, core_yyscan_t yyscanner)
void check_stack_depth(void)
Definition stack_depth.c:95
bool isnull
Definition parsenodes.h:391
union ValUnion val
Definition parsenodes.h:390
Definition pg_list.h:54
NodeTag type
Definition pg_list.h:55
Definition nodes.h:135
ParseLoc location
Definition primnodes.h:404
int paramid
Definition primnodes.h:397
ParamKind paramkind
Definition primnodes.h:396
const char * type
const char * name

◆ JUMBLE_ELEMENTS

#define JUMBLE_ELEMENTS (   list,
  node 
)     _jumbleElements(jstate, (List *) expr->list, node)

Definition at line 532 of file queryjumblefuncs.c.

◆ JUMBLE_FIELD

#define JUMBLE_FIELD (   item)
Value:
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)); \
AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)); \
} while (0)

Definition at line 536 of file queryjumblefuncs.c.

537 { \
538 if (sizeof(expr->item) == 8) \
539 AppendJumble64(jstate, (const unsigned char *) &(expr->item)); \
540 else if (sizeof(expr->item) == 4) \
541 AppendJumble32(jstate, (const unsigned char *) &(expr->item)); \
542 else if (sizeof(expr->item) == 2) \
543 AppendJumble16(jstate, (const unsigned char *) &(expr->item)); \
544 else if (sizeof(expr->item) == 1) \
545 AppendJumble8(jstate, (const unsigned char *) &(expr->item)); \
547 AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)); \
548} while (0)

◆ JUMBLE_LOCATION

#define JUMBLE_LOCATION (   location)     RecordConstLocation(jstate, false, expr->location, -1)

Definition at line 534 of file queryjumblefuncs.c.

◆ JUMBLE_NODE

#define JUMBLE_NODE (   item)     _jumbleNode(jstate, (Node *) expr->item)

Definition at line 530 of file queryjumblefuncs.c.

◆ JUMBLE_SIZE

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

Definition at line 51 of file queryjumblefuncs.c.

◆ JUMBLE_STRING

#define JUMBLE_STRING (   str)
Value:
do { \
if (expr->str) \
AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
} while(0)

Definition at line 549 of file queryjumblefuncs.c.

550 { \
551 if (expr->str) \
552 AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
555} while(0)

Function Documentation

◆ _jumbleA_Const()

static void _jumbleA_Const ( JumbleState jstate,
Node node 
)
static

Definition at line 712 of file queryjumblefuncs.c.

713{
714 A_Const *expr = (A_Const *) node;
715
716 JUMBLE_FIELD(isnull);
717 if (!expr->isnull)
718 {
719 JUMBLE_FIELD(val.node.type);
720 switch (nodeTag(&expr->val))
721 {
722 case T_Integer:
723 JUMBLE_FIELD(val.ival.ival);
724 break;
725 case T_Float:
726 JUMBLE_STRING(val.fval.fval);
727 break;
728 case T_Boolean:
729 JUMBLE_FIELD(val.boolval.boolval);
730 break;
731 case T_String:
732 JUMBLE_STRING(val.sval.sval);
733 break;
734 case T_BitString:
735 JUMBLE_STRING(val.bsval.bsval);
736 break;
737 default:
738 elog(ERROR, "unrecognized node type: %d",
739 (int) nodeTag(&expr->val));
740 break;
741 }
742 }
743}

References elog, ERROR, fb(), A_Const::isnull, JUMBLE_FIELD, JUMBLE_STRING, nodeTag, A_Const::val, and val.

◆ _jumbleElements()

static void _jumbleElements ( JumbleState jstate,
List elements,
Node node 
)
static

Definition at line 649 of file queryjumblefuncs.c.

650{
651 bool normalize_list = false;
652
653 if (IsSquashableConstantList(elements))
654 {
655 if (IsA(node, ArrayExpr))
656 {
657 ArrayExpr *aexpr = (ArrayExpr *) node;
658
659 if (aexpr->list_start > 0 && aexpr->list_end > 0)
660 {
662 false,
663 aexpr->list_start + 1,
664 (aexpr->list_end - aexpr->list_start) - 1);
665 normalize_list = true;
666 jstate->has_squashed_lists = true;
667 }
668 }
669 }
670
671 if (!normalize_list)
672 {
673 _jumbleNode(jstate, (Node *) elements);
674 }
675}

References _jumbleNode(), fb(), IsA, IsSquashableConstantList(), and RecordConstLocation().

◆ _jumbleList()

static void _jumbleList ( JumbleState jstate,
Node node 
)
static

Definition at line 608 of file queryjumblefuncs.c.

609{
610 List *expr = (List *) node;
611 ListCell *l;
612
613 switch (expr->type)
614 {
615 case T_List:
616 foreach(l, expr)
618 break;
619 case T_IntList:
620 foreach(l, expr)
621 AppendJumble32(jstate, (const unsigned char *) &lfirst_int(l));
622 break;
623 case T_OidList:
624 foreach(l, expr)
625 AppendJumble32(jstate, (const unsigned char *) &lfirst_oid(l));
626 break;
627 case T_XidList:
628 foreach(l, expr)
629 AppendJumble32(jstate, (const unsigned char *) &lfirst_xid(l));
630 break;
631 default:
632 elog(ERROR, "unrecognized list node type: %d",
633 (int) expr->type);
634 return;
635 }
636}

References _jumbleNode(), AppendJumble32(), elog, ERROR, fb(), lfirst, lfirst_int, lfirst_oid, lfirst_xid, and List::type.

Referenced by _jumbleNode().

◆ _jumbleNode()

static void _jumbleNode ( JumbleState jstate,
Node node 
)
static

Definition at line 563 of file queryjumblefuncs.c.

564{
565 Node *expr = node;
566#ifdef USE_ASSERT_CHECKING
567 Size prev_jumble_len = jstate->total_jumble_len;
568#endif
569
570 if (expr == NULL)
571 {
573 return;
574 }
575
576 /* Guard against stack overflow due to overly complex expressions */
578
579 /*
580 * We always emit the node's NodeTag, then any additional fields that are
581 * considered significant, and then we recurse to any child nodes.
582 */
584
585 switch (nodeTag(expr))
586 {
587#include "queryjumblefuncs.switch.c"
588
589 case T_List:
590 case T_IntList:
591 case T_OidList:
592 case T_XidList:
593 _jumbleList(jstate, expr);
594 break;
595
596 default:
597 /* Only a warning, since we can stumble along anyway */
598 elog(WARNING, "unrecognized node type: %d",
599 (int) nodeTag(expr));
600 break;
601 }
602
603 /* Ensure we added something to the jumble buffer */
604 Assert(jstate->total_jumble_len > prev_jumble_len);
605}

References _jumbleList(), AppendJumbleNull(), Assert, check_stack_depth(), elog, fb(), JUMBLE_FIELD, nodeTag, type, and WARNING.

Referenced by _jumbleElements(), _jumbleList(), and DoJumble().

◆ _jumbleParam()

static void _jumbleParam ( JumbleState jstate,
Node node 
)
static

Definition at line 682 of file queryjumblefuncs.c.

683{
684 Param *expr = (Param *) node;
685
686 JUMBLE_FIELD(paramkind);
687 JUMBLE_FIELD(paramid);
688 JUMBLE_FIELD(paramtype);
689 /* paramtypmod and paramcollid are ignored */
690
691 if (expr->paramkind == PARAM_EXTERN)
692 {
693 /*
694 * At this point, only external parameter locations outside of
695 * squashable lists will be recorded.
696 */
697 RecordConstLocation(jstate, true, expr->location, -1);
698
699 /*
700 * Update the highest Param id seen, in order to start normalization
701 * correctly.
702 *
703 * Note: This value is reset at the end of jumbling if there exists a
704 * squashable list. See the comment in the definition of JumbleState.
705 */
706 if (expr->paramid > jstate->highest_extern_param_id)
707 jstate->highest_extern_param_id = expr->paramid;
708 }
709}

References fb(), JUMBLE_FIELD, Param::location, PARAM_EXTERN, Param::paramid, Param::paramkind, and RecordConstLocation().

◆ _jumbleRangeTblEntry_eref()

static void _jumbleRangeTblEntry_eref ( JumbleState jstate,
RangeTblEntry rte,
Alias expr 
)
static

Definition at line 767 of file queryjumblefuncs.c.

770{
772
773 /*
774 * This includes only the table name, the list of column names is ignored.
775 */
776 JUMBLE_STRING(aliasname);
777}

References JUMBLE_FIELD, JUMBLE_STRING, and type.

◆ _jumbleVariableSetStmt()

static void _jumbleVariableSetStmt ( JumbleState jstate,
Node node 
)
static

Definition at line 746 of file queryjumblefuncs.c.

747{
748 VariableSetStmt *expr = (VariableSetStmt *) node;
749
750 JUMBLE_FIELD(kind);
752
753 /*
754 * Account for the list of arguments in query jumbling only if told by the
755 * parser.
756 */
757 if (expr->jumble_args)
758 JUMBLE_NODE(args);
759 JUMBLE_FIELD(is_local);
760 JUMBLE_LOCATION(location);
761}

References VariableSetStmt::jumble_args, JUMBLE_FIELD, JUMBLE_LOCATION, JUMBLE_NODE, JUMBLE_STRING, and name.

◆ AppendJumble()

static pg_noinline void AppendJumble ( JumbleState jstate,
const unsigned char value,
Size  size 
)
static

Definition at line 299 of file queryjumblefuncs.c.

300{
301 if (jstate->pending_nulls > 0)
303
305}
static struct @177 value
static pg_attribute_always_inline void AppendJumbleInternal(JumbleState *jstate, const unsigned char *item, Size size)
static void FlushPendingNulls(JumbleState *jstate)

References AppendJumbleInternal(), fb(), FlushPendingNulls(), and value.

◆ AppendJumble16()

static pg_noinline void AppendJumble16 ( JumbleState jstate,
const unsigned char value 
)
static

Definition at line 336 of file queryjumblefuncs.c.

337{
338 if (jstate->pending_nulls > 0)
340
342}

References AppendJumbleInternal(), fb(), FlushPendingNulls(), and value.

◆ AppendJumble32()

static pg_noinline void AppendJumble32 ( JumbleState jstate,
const unsigned char value 
)
static

Definition at line 350 of file queryjumblefuncs.c.

351{
352 if (jstate->pending_nulls > 0)
354
356}

References AppendJumbleInternal(), fb(), FlushPendingNulls(), and value.

Referenced by _jumbleList().

◆ AppendJumble64()

static pg_noinline void AppendJumble64 ( JumbleState jstate,
const unsigned char value 
)
static

Definition at line 364 of file queryjumblefuncs.c.

365{
366 if (jstate->pending_nulls > 0)
368
370}

References AppendJumbleInternal(), fb(), FlushPendingNulls(), and value.

◆ AppendJumble8()

static pg_noinline void AppendJumble8 ( JumbleState jstate,
const unsigned char value 
)
static

Definition at line 322 of file queryjumblefuncs.c.

323{
324 if (jstate->pending_nulls > 0)
326
328}

References AppendJumbleInternal(), fb(), FlushPendingNulls(), and value.

◆ AppendJumbleInternal()

static pg_attribute_always_inline void AppendJumbleInternal ( JumbleState jstate,
const unsigned char item,
Size  size 
)
static

Definition at line 236 of file queryjumblefuncs.c.

238{
239 unsigned char *jumble = jstate->jumble;
240 Size jumble_len = jstate->jumble_len;
241
242 /* Ensure the caller didn't mess up */
243 Assert(size > 0);
244
245 /*
246 * Fast path for when there's enough space left in the buffer. This is
247 * worthwhile as means the memcpy can be inlined into very efficient code
248 * when 'size' is a compile-time constant.
249 */
250 if (likely(size <= JUMBLE_SIZE - jumble_len))
251 {
252 memcpy(jumble + jumble_len, item, size);
253 jstate->jumble_len += size;
254
255#ifdef USE_ASSERT_CHECKING
256 jstate->total_jumble_len += size;
257#endif
258
259 return;
260 }
261
262 /*
263 * Whenever the jumble buffer is full, we hash the current contents and
264 * reset the buffer to contain just that hash value, thus relying on the
265 * hash to summarize everything so far.
266 */
267 do
268 {
270
271 if (unlikely(jumble_len >= JUMBLE_SIZE))
272 {
274
276 JUMBLE_SIZE, 0));
277 memcpy(jumble, &start_hash, sizeof(start_hash));
278 jumble_len = sizeof(start_hash);
279 }
280 part_size = Min(size, JUMBLE_SIZE - jumble_len);
281 memcpy(jumble + jumble_len, item, part_size);
282 jumble_len += part_size;
283 item += part_size;
284 size -= part_size;
285
286#ifdef USE_ASSERT_CHECKING
287 jstate->total_jumble_len += part_size;
288#endif
289 } while (size > 0);
290
291 jstate->jumble_len = jumble_len;
292}
#define Min(x, y)
Definition c.h:1091
#define likely(x)
Definition c.h:437
int64_t int64
Definition c.h:621
#define unlikely(x)
Definition c.h:438
static Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition hashfn.h:37
static int64 DatumGetInt64(Datum X)
Definition postgres.h:416
#define JUMBLE_SIZE

References Assert, DatumGetInt64(), fb(), hash_any_extended(), JUMBLE_SIZE, likely, memcpy(), Min, and unlikely.

Referenced by AppendJumble(), AppendJumble16(), AppendJumble32(), AppendJumble64(), AppendJumble8(), and FlushPendingNulls().

◆ AppendJumbleNull()

static pg_attribute_always_inline void AppendJumbleNull ( JumbleState jstate)
static

Definition at line 312 of file queryjumblefuncs.c.

313{
314 jstate->pending_nulls++;
315}

References fb().

Referenced by _jumbleNode().

◆ CleanQuerytext()

const char * CleanQuerytext ( const char query,
int location,
int len 
)

Definition at line 88 of file queryjumblefuncs.c.

89{
90 int query_location = *location;
91 int query_len = *len;
92
93 /* First apply starting offset, unless it's -1 (unknown). */
94 if (query_location >= 0)
95 {
96 Assert(query_location <= strlen(query));
97 query += query_location;
98 /* Length of 0 (or -1) means "rest of string" */
99 if (query_len <= 0)
100 query_len = strlen(query);
101 else
102 Assert(query_len <= strlen(query));
103 }
104 else
105 {
106 /* If query location is unknown, distrust query_len as well */
107 query_location = 0;
108 query_len = strlen(query);
109 }
110
111 /*
112 * Discard leading and trailing whitespace, too. Use scanner_isspace()
113 * not libc's isspace(), because we want to match the lexer's behavior.
114 *
115 * Note: the parser now strips leading comments and whitespace from the
116 * reported stmt_location, so this first loop will only iterate in the
117 * unusual case that the location didn't propagate to here. But the
118 * statement length will extend to the end-of-string or terminating
119 * semicolon, so the second loop often does something useful.
120 */
121 while (query_len > 0 && scanner_isspace(query[0]))
122 query++, query_location++, query_len--;
123 while (query_len > 0 && scanner_isspace(query[query_len - 1]))
124 query_len--;
125
126 *location = query_location;
127 *len = query_len;
128
129 return query;
130}
const void size_t len
bool scanner_isspace(char ch)
Definition scansup.c:105

References Assert, fb(), len, and scanner_isspace().

Referenced by pgss_store(), and script_error_callback().

◆ CompLocation()

static int CompLocation ( const void a,
const void b 
)
static

Definition at line 783 of file queryjumblefuncs.c.

784{
785 int l = ((const LocationLen *) a)->location;
786 int r = ((const LocationLen *) b)->location;
787
788 return pg_cmp_s32(l, r);
789}

References a, b, and pg_cmp_s32().

Referenced by ComputeConstantLengths().

◆ ComputeConstantLengths()

LocationLen * ComputeConstantLengths ( const JumbleState jstate,
const char query,
int  query_loc 
)

Definition at line 822 of file queryjumblefuncs.c.

824{
826 core_yyscan_t yyscanner;
830
831 if (jstate->clocations_count == 0)
832 return NULL;
833
834 /* Copy constant locations to avoid modifying jstate */
835 locs = palloc_array(LocationLen, jstate->clocations_count);
836 memcpy(locs, jstate->clocations, jstate->clocations_count * sizeof(LocationLen));
837
838 /*
839 * Sort the records by location so that we can process them in order while
840 * scanning the query text.
841 */
842 if (jstate->clocations_count > 1)
843 qsort(locs, jstate->clocations_count,
844 sizeof(LocationLen), CompLocation);
845
846 /* initialize the flex scanner --- should match raw_parser() */
847 yyscanner = scanner_init(query,
848 &yyextra,
851
852 /* Search for each constant, in sequence */
853 for (int i = 0; i < jstate->clocations_count; i++)
854 {
855 int loc;
856 int tok;
857
858 /* Ignore constants after the first one in the same location */
859 if (i > 0 && locs[i].location == locs[i - 1].location)
860 {
861 locs[i].length = -1;
862 continue;
863 }
864
865 if (locs[i].squashed)
866 continue; /* squashable list, ignore */
867
868 /*
869 * Adjust the constant's location using the provided starting location
870 * of the current statement. This allows us to avoid scanning a
871 * multi-statement string from the beginning.
872 */
873 loc = locs[i].location - query_loc;
874 Assert(loc >= 0);
875
876 /*
877 * We have a valid location for a constant that's not a dupe. Lex
878 * tokens until we find the desired constant.
879 */
880 for (;;)
881 {
882 tok = core_yylex(&yylval, &yylloc, yyscanner);
883
884 /* We should not hit end-of-string, but if we do, behave sanely */
885 if (tok == 0)
886 break; /* out of inner for-loop */
887
888 /*
889 * We should find the token position exactly, but if we somehow
890 * run past it, work with that.
891 */
892 if (yylloc >= loc)
893 {
894 if (query[loc] == '-')
895 {
896 /*
897 * It's a negative value - this is the one and only case
898 * where we replace more than a single token.
899 *
900 * Do not compensate for the special-case adjustment of
901 * location to that of the leading '-' operator in the
902 * event of a negative constant (see doNegate() in
903 * gram.y). It is also useful for our purposes to start
904 * from the minus symbol. In this way, queries like
905 * "select * from foo where bar = 1" and "select * from
906 * foo where bar = -2" can be treated similarly.
907 */
908 tok = core_yylex(&yylval, &yylloc, yyscanner);
909 if (tok == 0)
910 break; /* out of inner for-loop */
911 }
912
913 /*
914 * We now rely on the assumption that flex has placed a zero
915 * byte after the text of the current token in scanbuf.
916 */
917 locs[i].length = strlen(yyextra.scanbuf + loc);
918 break; /* out of inner for-loop */
919 }
920 }
921
922 /* If we hit end-of-string, give up, leaving remaining lengths -1 */
923 if (tok == 0)
924 break;
925 }
926
927 scanner_finish(yyscanner);
928
929 return locs;
930}

References Assert, CompLocation(), core_yylex(), fb(), i, memcpy(), palloc_array, qsort, ScanKeywords, ScanKeywordTokens, scanner_finish(), scanner_init(), yyextra, and yylloc.

Referenced by generate_normalized_query().

◆ DoJumble()

static int64 DoJumble ( JumbleState jstate,
Node node 
)
static

Definition at line 211 of file queryjumblefuncs.c.

212{
213 /* Jumble the given node */
214 _jumbleNode(jstate, node);
215
216 /* Flush any pending NULLs before doing the final hash */
217 if (jstate->pending_nulls > 0)
219
220 /* Squashed list found, reset highest_extern_param_id */
221 if (jstate->has_squashed_lists)
222 jstate->highest_extern_param_id = 0;
223
224 /* Process the jumble buffer and produce the hash value */
226 jstate->jumble_len,
227 0));
228}

References _jumbleNode(), DatumGetInt64(), fb(), FlushPendingNulls(), and hash_any_extended().

Referenced by JumbleQuery().

◆ EnableQueryId()

void EnableQueryId ( void  )

Definition at line 171 of file queryjumblefuncs.c.

172{
174 query_id_enabled = true;
175}
@ COMPUTE_QUERY_ID_OFF
Definition queryjumble.h:83
bool query_id_enabled
int compute_query_id

References compute_query_id, COMPUTE_QUERY_ID_OFF, and query_id_enabled.

Referenced by _PG_init().

◆ FlushPendingNulls()

static pg_attribute_always_inline void FlushPendingNulls ( JumbleState jstate)
static

Definition at line 379 of file queryjumblefuncs.c.

380{
381 Assert(jstate->pending_nulls > 0);
382
384 (const unsigned char *) &jstate->pending_nulls, 4);
385 jstate->pending_nulls = 0;
386}

References AppendJumbleInternal(), Assert, and fb().

Referenced by AppendJumble(), AppendJumble16(), AppendJumble32(), AppendJumble64(), AppendJumble8(), and DoJumble().

◆ InitJumble()

static JumbleState * InitJumble ( void  )
static

Definition at line 182 of file queryjumblefuncs.c.

183{
185
187
188 /* Set up workspace for query jumbling */
189 jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
190 jstate->jumble_len = 0;
191 jstate->clocations_buf_size = 32;
192 jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size *
193 sizeof(LocationLen));
194 jstate->clocations_count = 0;
195 jstate->highest_extern_param_id = 0;
196 jstate->pending_nulls = 0;
197 jstate->has_squashed_lists = false;
198#ifdef USE_ASSERT_CHECKING
199 jstate->total_jumble_len = 0;
200#endif
201
202 return jstate;
203}
#define palloc_object(type)
Definition fe_memutils.h:74
void * palloc(Size size)
Definition mcxt.c:1387

References fb(), JUMBLE_SIZE, palloc(), and palloc_object.

Referenced by JumbleQuery().

◆ IsSquashableConstant()

static bool IsSquashableConstant ( Node element)
static

Definition at line 439 of file queryjumblefuncs.c.

440{
441restart:
442 switch (nodeTag(element))
443 {
444 case T_RelabelType:
445 /* Unwrap RelabelType */
446 element = (Node *) ((RelabelType *) element)->arg;
447 goto restart;
448
449 case T_CoerceViaIO:
450 /* Unwrap CoerceViaIO */
451 element = (Node *) ((CoerceViaIO *) element)->arg;
452 goto restart;
453
454 case T_Const:
455 return true;
456
457 case T_Param:
458 return castNode(Param, element)->paramkind == PARAM_EXTERN;
459
460 case T_FuncExpr:
461 {
462 FuncExpr *func = (FuncExpr *) element;
463 ListCell *temp;
464
465 if (func->funcformat != COERCE_IMPLICIT_CAST &&
466 func->funcformat != COERCE_EXPLICIT_CAST)
467 return false;
468
469 if (func->funcid > FirstGenbkiObjectId)
470 return false;
471
472 /*
473 * We can check function arguments recursively, being careful
474 * about recursing too deep. At each recursion level it's
475 * enough to test the stack on the first element. (Note that
476 * I wasn't able to hit this without bloating the stack
477 * artificially in this function: the parser errors out before
478 * stack size becomes a problem here.)
479 */
480 foreach(temp, func->args)
481 {
482 Node *arg = lfirst(temp);
483
484 if (!IsA(arg, Const))
485 {
486 if (foreach_current_index(temp) == 0 &&
488 return false;
489 else if (!IsSquashableConstant(arg))
490 return false;
491 }
492 }
493
494 return true;
495 }
496
497 default:
498 return false;
499 }
500}
Datum arg
Definition elog.c:1323
#define castNode(_type_, nodeptr)
Definition nodes.h:182
#define foreach_current_index(var_or_cell)
Definition pg_list.h:435
@ COERCE_IMPLICIT_CAST
Definition primnodes.h:769
@ COERCE_EXPLICIT_CAST
Definition primnodes.h:768
static bool IsSquashableConstant(Node *element)
static chr element(struct vars *v, const chr *startp, const chr *endp)
bool stack_is_too_deep(void)
Oid funcid
Definition primnodes.h:783
List * args
Definition primnodes.h:801
#define FirstGenbkiObjectId
Definition transam.h:195

References arg, FuncExpr::args, castNode, COERCE_EXPLICIT_CAST, COERCE_IMPLICIT_CAST, element(), fb(), FirstGenbkiObjectId, foreach_current_index, FuncExpr::funcid, IsA, IsSquashableConstant(), lfirst, nodeTag, PARAM_EXTERN, and stack_is_too_deep().

Referenced by IsSquashableConstant(), and IsSquashableConstantList().

◆ IsSquashableConstantList()

static bool IsSquashableConstantList ( List elements)
static

Definition at line 513 of file queryjumblefuncs.c.

514{
515 ListCell *temp;
516
517 /* If the list is too short, we don't try to squash it. */
518 if (list_length(elements) < 2)
519 return false;
520
521 foreach(temp, elements)
522 {
524 return false;
525 }
526
527 return true;
528}
static int list_length(const List *l)
Definition pg_list.h:152

References fb(), IsSquashableConstant(), lfirst, and list_length().

Referenced by _jumbleElements().

◆ JumbleQuery()

JumbleState * JumbleQuery ( Query query)

Definition at line 139 of file queryjumblefuncs.c.

140{
142
144
145 jstate = InitJumble();
146
147 query->queryId = DoJumble(jstate, (Node *) query);
148
149 /*
150 * If we are unlucky enough to get a hash of zero, use 1 instead for
151 * normal statements and 2 for utility queries.
152 */
153 if (query->queryId == INT64CONST(0))
154 {
155 if (query->utilityStmt)
156 query->queryId = INT64CONST(2);
157 else
158 query->queryId = INT64CONST(1);
159 }
160
161 return jstate;
162}
#define INT64CONST(x)
Definition c.h:630
static bool IsQueryIdEnabled(void)
static int64 DoJumble(JumbleState *jstate, Node *node)
static JumbleState * InitJumble(void)
Node * utilityStmt
Definition parsenodes.h:141

References Assert, DoJumble(), fb(), InitJumble(), INT64CONST, IsQueryIdEnabled(), and Query::utilityStmt.

Referenced by ExecCreateTableAs(), ExplainOneUtility(), ExplainQuery(), parse_analyze_fixedparams(), parse_analyze_varparams(), parse_analyze_withcb(), and PerformCursorOpen().

◆ RecordConstLocation()

static void RecordConstLocation ( JumbleState jstate,
bool  extern_param,
int  location,
int  len 
)
static

Definition at line 400 of file queryjumblefuncs.c.

401{
402 /* -1 indicates unknown or undefined location */
403 if (location >= 0)
404 {
405 /* enlarge array if needed */
406 if (jstate->clocations_count >= jstate->clocations_buf_size)
407 {
408 jstate->clocations_buf_size *= 2;
409 jstate->clocations = (LocationLen *)
410 repalloc(jstate->clocations,
411 jstate->clocations_buf_size *
412 sizeof(LocationLen));
413 }
414 jstate->clocations[jstate->clocations_count].location = location;
415
416 /*
417 * Lengths are either positive integers (indicating a squashable
418 * list), or -1.
419 */
420 Assert(len > -1 || len == -1);
421 jstate->clocations[jstate->clocations_count].length = len;
422 jstate->clocations[jstate->clocations_count].squashed = (len > -1);
423 jstate->clocations[jstate->clocations_count].extern_param = extern_param;
424 jstate->clocations_count++;
425 }
426}
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632

References Assert, fb(), len, and repalloc().

Referenced by _jumbleElements(), and _jumbleParam().

Variable Documentation

◆ compute_query_id

int compute_query_id = COMPUTE_QUERY_ID_AUTO

Definition at line 54 of file queryjumblefuncs.c.

Referenced by EnableQueryId(), ExplainPrintPlan(), and IsQueryIdEnabled().

◆ query_id_enabled

bool query_id_enabled = false

Definition at line 63 of file queryjumblefuncs.c.

Referenced by EnableQueryId(), and IsQueryIdEnabled().