From 115199e32db7c9789b27ffc60ef76d2473154a99 Mon Sep 17 00:00:00 2001 From: Sami Imseih Date: Mon, 31 Mar 2025 09:59:16 -0700 Subject: [PATCH v4] Allow plugins to Jumble an expression. This change makes _jumbleNode available to plugins that wish to perform jumbling on nodes other than Query. To facilitate this capability, two new routines to initialize a jumble state and to produce a 64-bit hash from the jumble are also made available. It may also be a good idea to separate these aforementioned routines into a separate C file, as they can be used for more than query jumbling. That could be done in a future change. Discussion: https://www.postgresql.org/message-id/Z9khOo14yzZHCnmn%40paquier.xyz --- src/backend/nodes/queryjumblefuncs.c | 118 ++++++++++++++------------- src/include/nodes/queryjumble.h | 49 +++++++++++ 2 files changed, 110 insertions(+), 57 deletions(-) diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c index 513cf92d357..7d932d43d4f 100644 --- a/src/backend/nodes/queryjumblefuncs.c +++ b/src/backend/nodes/queryjumblefuncs.c @@ -55,14 +55,11 @@ int compute_query_id = COMPUTE_QUERY_ID_AUTO; */ bool query_id_enabled = false; -static JumbleState *InitJumble(void); +static JumbleState *InitJumbleInternal(bool record_clocations); static uint64 DoJumble(JumbleState *jstate, Node *node); -static void AppendJumble(JumbleState *jstate, - const unsigned char *item, Size size); static void FlushPendingNulls(JumbleState *jstate); static void RecordConstLocation(JumbleState *jstate, int location, bool merged); -static void _jumbleNode(JumbleState *jstate, Node *node); static void _jumbleElements(JumbleState *jstate, List *elements); static void _jumbleA_Const(JumbleState *jstate, Node *node); static void _jumbleList(JumbleState *jstate, Node *node); @@ -133,7 +130,7 @@ JumbleQuery(Query *query) Assert(IsQueryIdEnabled()); - jstate = InitJumble(); + jstate = InitJumbleInternal(true); query->queryId = DoJumble(jstate, (Node *) query); @@ -166,11 +163,11 @@ EnableQueryId(void) } /* - * InitJumble + * InitJumbleInternal * Allocate a JumbleState object and make it ready to jumble. */ static JumbleState * -InitJumble(void) +InitJumbleInternal(bool record_clocations) { JumbleState *jstate; @@ -179,9 +176,19 @@ InitJumble(void) /* 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)); + + if (record_clocations) + { + jstate->clocations_buf_size = 32; + jstate->clocations = (LocationLen *) + palloc(jstate->clocations_buf_size * sizeof(LocationLen)); + } + else + { + jstate->clocations_buf_size = 0; + jstate->clocations = NULL; + } + jstate->clocations_count = 0; jstate->highest_extern_param_id = 0; jstate->pending_nulls = 0; @@ -193,16 +200,21 @@ InitJumble(void) } /* - * DoJumble - * Jumble the given Node using the given JumbleState and return the resulting - * jumble hash. + * Exported initializer for jumble state that allows plugins to hash values and + * nodes, but does not record constant locations, for now. */ -static uint64 -DoJumble(JumbleState *jstate, Node *node) +JumbleState * +InitJumble() { - /* Jumble the given node */ - _jumbleNode(jstate, node); + return InitJumbleInternal(false); +} +/* + * Produce a 64-bit hash from a jumble state. + */ +uint64 +HashJumbleState(JumbleState *jstate) +{ /* Flush any pending NULLs before doing the final hash */ if (jstate->pending_nulls > 0) FlushPendingNulls(jstate); @@ -213,6 +225,20 @@ DoJumble(JumbleState *jstate, Node *node) 0)); } +/* + * DoJumble + * Jumble the given Node using the given JumbleState and return the resulting + * jumble hash. + */ +static uint64 +DoJumble(JumbleState *jstate, Node *node) +{ + /* Jumble the given node */ + JumbleNode(jstate, node); + + return HashJumbleState(jstate); +} + /* * AppendJumbleInternal: Internal function for appending to the jumble buffer * @@ -281,7 +307,7 @@ AppendJumbleInternal(JumbleState *jstate, const unsigned char *item, * AppendJumble * Add 'size' bytes of the given jumble 'value' to the jumble state */ -static pg_noinline void +pg_noinline void AppendJumble(JumbleState *jstate, const unsigned char *value, Size size) { if (jstate->pending_nulls > 0) @@ -290,21 +316,11 @@ AppendJumble(JumbleState *jstate, const unsigned char *value, Size size) 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 +pg_noinline void AppendJumble8(JumbleState *jstate, const unsigned char *value) { if (jstate->pending_nulls > 0) @@ -318,7 +334,7 @@ AppendJumble8(JumbleState *jstate, const unsigned char *value) * Add the first 2 bytes from the given 'value' pointer to the jumble * state. */ -static pg_noinline void +pg_noinline void AppendJumble16(JumbleState *jstate, const unsigned char *value) { if (jstate->pending_nulls > 0) @@ -332,7 +348,7 @@ AppendJumble16(JumbleState *jstate, const unsigned char *value) * Add the first 4 bytes from the given 'value' pointer to the jumble * state. */ -static pg_noinline void +pg_noinline void AppendJumble32(JumbleState *jstate, const unsigned char *value) { if (jstate->pending_nulls > 0) @@ -346,7 +362,7 @@ AppendJumble32(JumbleState *jstate, const unsigned char *value) * Add the first 8 bytes from the given 'value' pointer to the jumble * state. */ -static pg_noinline void +pg_noinline void AppendJumble64(JumbleState *jstate, const unsigned char *value) { if (jstate->pending_nulls > 0) @@ -383,6 +399,10 @@ FlushPendingNulls(JumbleState *jstate) static void RecordConstLocation(JumbleState *jstate, int location, bool squashed) { + /* Skip if the caller is a plugin not interested in constant locations */ + if (jstate->clocations == NULL) + return; + /* -1 indicates unknown or undefined location */ if (location >= 0) { @@ -485,31 +505,15 @@ IsSquashableConstList(List *elements, Node **firstExpr, Node **lastExpr) } #define JUMBLE_NODE(item) \ - _jumbleNode(jstate, (Node *) expr->item) + JumbleNode(jstate, (Node *) expr->item) +#define JUMBLE_FIELD(item) \ + JUMBLE_VALUE(expr->item) +#define JUMBLE_STRING(str) \ + JUMBLE_VALUE_STRING(expr->str) #define JUMBLE_ELEMENTS(list) \ _jumbleElements(jstate, (List *) expr->list) #define JUMBLE_LOCATION(location) \ RecordConstLocation(jstate, expr->location, false) -#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) @@ -548,12 +552,12 @@ _jumbleElements(JumbleState *jstate, List *elements) } else { - _jumbleNode(jstate, (Node *) elements); + JumbleNode(jstate, (Node *) elements); } } -static void -_jumbleNode(JumbleState *jstate, Node *node) +void +JumbleNode(JumbleState *jstate, Node *node) { Node *expr = node; #ifdef USE_ASSERT_CHECKING @@ -627,7 +631,7 @@ _jumbleList(JumbleState *jstate, Node *node) { case T_List: foreach(l, expr) - _jumbleNode(jstate, lfirst(l)); + JumbleNode(jstate, lfirst(l)); break; case T_IntList: foreach(l, expr) diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h index da7c7abed2e..47f49a2f831 100644 --- a/src/include/nodes/queryjumble.h +++ b/src/include/nodes/queryjumble.h @@ -81,6 +81,55 @@ enum ComputeQueryIdType extern PGDLLIMPORT int compute_query_id; +/* + * Generic routines for expression jumbling. + * + * XXX: It may be better to separate these routines in a separate + * file. + */ +extern JumbleState *InitJumble(void); +extern void JumbleNode(JumbleState *jstate, Node *node); +extern uint64 HashJumbleState(JumbleState *jstate); + +extern void AppendJumble(JumbleState *jstate, + const unsigned char *item, Size size); +extern void AppendJumble8(JumbleState *jstate, const unsigned char *value); +extern void AppendJumble16(JumbleState *jstate, const unsigned char *value); +extern void AppendJumble32(JumbleState *jstate, const unsigned char *value); +extern void AppendJumble64(JumbleState *jstate, const unsigned char *value); + +/* + * AppendJumbleNull + * For jumbling NULL pointers + */ +static pg_attribute_always_inline void +AppendJumbleNull(JumbleState *jstate) +{ + jstate->pending_nulls++; +} + +#define JUMBLE_VALUE(val) \ +do { \ + if (sizeof(val) == 8) \ + AppendJumble64(jstate, (const unsigned char *) &(val)); \ + else if (sizeof(val) == 4) \ + AppendJumble32(jstate, (const unsigned char *) &(val)); \ + else if (sizeof(val) == 2) \ + AppendJumble16(jstate, (const unsigned char *) &(val)); \ + else if (sizeof(val) == 1) \ + AppendJumble8(jstate, (const unsigned char *) &(val)); \ + else \ + AppendJumble(jstate, (const unsigned char *) &(val), sizeof(val)); \ +} while (0) +#define JUMBLE_VALUE_STRING(str) \ +do { \ + if (str) \ + AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1); \ + else \ + AppendJumbleNull(jstate); \ +} while(0) + +/* Query jumbling routines */ extern const char *CleanQuerytext(const char *query, int *location, int *len); extern JumbleState *JumbleQuery(Query *query); extern void EnableQueryId(void); -- 2.47.1