public inbox for [email protected]
help / color / mirror / Atom feedFrom: Andrey V. Lepikhov <[email protected]>
To: Tom Lane <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Subject: Re: Multiple Query IDs for a rewritten parse tree
Date: Fri, 21 Jan 2022 11:33:22 +0500
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
<[email protected]>
On 1/9/22 5:49 AM, Tom Lane wrote:
> The idea I'd been vaguely thinking about is to allow attaching a list
> of query-hash nodes to a Query, where each node would contain a "tag"
> identifying the specific hash calculation method, and also the value
> of the query's hash calculated according to that method. We could
> probably get away with saying that all such hash values must be uint64.
> The main difference from your function-OID idea, I think, is that
> I'm envisioning the tags as being small integers with well-known
> values, similarly to the way we manage stakind values in pg_statistic.
> In this way, an extension that wants a hash that the core knows how
> to calculate doesn't need its own copy of the code, and similarly
> one extension could publish a calculation method for use by other
> extensions.
To move forward, I have made a patch that implements this idea (see
attachment). It is a POC, but passes all regression tests.
Registration of an queryId generator implemented by analogy with
extensible methods machinery. Also, I switched queryId to int64 type and
renamed to 'label'.
Some lessons learned:
1. Single queryId implementation is deeply tangled with the core code
(stat reporting machinery and parallel workers as an example).
2. We need a custom queryId, that is based on a generated queryId
(according to the logic of pg_stat_statements).
3. We should think about safety of de-registering procedure.
4. We should reserve position of default in-core generator and think on
logic of enabling/disabling it.
5. We should add an EXPLAIN hook, to allow an extension to print this
custom queryId.
--
regards,
Andrey Lepikhov
Postgres Professional
Attachments:
[text/x-patch] POC-multiple-query_id.patch (26.9K, ../[email protected]/2-POC-multiple-query_id.patch)
download | inline diff:
From f54bb60bd71b49ac8e1b85cd2ad86332a8a81e84 Mon Sep 17 00:00:00 2001
From: Andrey Lepikhov <[email protected]>
Date: Thu, 20 Jan 2022 16:05:17 +0500
Subject: [PATCH] Initial commit
---
.../pg_stat_statements/pg_stat_statements.c | 57 ++++--
src/backend/commands/explain.c | 11 +-
src/backend/executor/execMain.c | 4 +-
src/backend/executor/execParallel.c | 3 +-
src/backend/nodes/copyfuncs.c | 21 ++-
src/backend/nodes/outfuncs.c | 17 +-
src/backend/nodes/readfuncs.c | 17 +-
src/backend/optimizer/plan/planner.c | 2 +-
src/backend/parser/analyze.c | 15 +-
src/backend/rewrite/rewriteHandler.c | 4 +-
src/backend/tcop/postgres.c | 11 +-
src/backend/utils/misc/guc.c | 2 +-
src/backend/utils/misc/queryjumble.c | 164 ++++++++++++++++--
src/include/nodes/nodes.h | 1 +
src/include/nodes/parsenodes.h | 10 +-
src/include/nodes/plannodes.h | 2 +-
src/include/parser/analyze.h | 3 +-
src/include/utils/queryjumble.h | 12 +-
18 files changed, 294 insertions(+), 62 deletions(-)
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 082bfa8f77..ebfc3331df 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -307,8 +307,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_info);
static void pgss_shmem_startup(void);
static void pgss_shmem_shutdown(int code, Datum arg);
-static void pgss_post_parse_analyze(ParseState *pstate, Query *query,
- JumbleState *jstate);
+static void pgss_post_parse_analyze(ParseState *pstate, Query *query);
static PlannedStmt *pgss_planner(Query *parse,
const char *query_string,
int cursorOptions,
@@ -813,13 +812,29 @@ error:
}
/*
- * Post-parse-analysis hook: mark query with a queryId
+ * Post-parse-analysis hook: create a label for the query.
*/
static void
-pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
+pgss_post_parse_analyze(ParseState *pstate, Query *query)
{
+ JumbleState *jstate;
+ QueryLabel *label = get_query_label(query->queryIds, 0);
+
if (prev_post_parse_analyze_hook)
- prev_post_parse_analyze_hook(pstate, query, jstate);
+ prev_post_parse_analyze_hook(pstate, query);
+
+ if (label)
+ {
+ add_custom_query_label(&query->queryIds, -1, label->hash);
+ jstate = (JumbleState *) label->context;
+ }
+ else
+ {
+ add_custom_query_label(&query->queryIds, -1, UINT64CONST(0));
+ jstate = NULL;
+ }
+
+ label = get_query_label(query->queryIds, -1);
/* Safety check... */
if (!pgss || !pgss_hash || !pgss_enabled(exec_nested_level))
@@ -833,7 +848,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
if (query->utilityStmt)
{
if (pgss_track_utility && !PGSS_HANDLED_UTILITY(query->utilityStmt))
- query->queryId = UINT64CONST(0);
+ label->hash = UINT64CONST(0);
return;
}
@@ -846,7 +861,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
*/
if (jstate && jstate->clocations_count > 0)
pgss_store(pstate->p_sourcetext,
- query->queryId,
+ label->hash,
query->stmt_location,
query->stmt_len,
PGSS_INVALID,
@@ -868,6 +883,9 @@ pgss_planner(Query *parse,
ParamListInfo boundParams)
{
PlannedStmt *result;
+ int64 queryId;
+
+ queryId = get_query_label_hash(parse->queryIds, -1);
/*
* We can't process the query if no query_string is provided, as
@@ -883,7 +901,7 @@ pgss_planner(Query *parse,
*/
if (pgss_enabled(plan_nested_level + exec_nested_level)
&& pgss_track_planning && query_string
- && parse->queryId != UINT64CONST(0))
+ && queryId != UINT64CONST(0))
{
instr_time start;
instr_time duration;
@@ -930,7 +948,7 @@ pgss_planner(Query *parse,
WalUsageAccumDiff(&walusage, &pgWalUsage, &walusage_start);
pgss_store(query_string,
- parse->queryId,
+ queryId,
parse->stmt_location,
parse->stmt_len,
PGSS_PLAN,
@@ -959,17 +977,21 @@ pgss_planner(Query *parse,
static void
pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
{
+ int64 queryId;
+
if (prev_ExecutorStart)
prev_ExecutorStart(queryDesc, eflags);
else
standard_ExecutorStart(queryDesc, eflags);
+ queryId = get_query_label_hash(queryDesc->plannedstmt->queryIds, -1);
+
/*
* If query has queryId zero, don't track it. This prevents double
* counting of optimizable statements that are directly contained in
* utility statements.
*/
- if (pgss_enabled(exec_nested_level) && queryDesc->plannedstmt->queryId != UINT64CONST(0))
+ if (pgss_enabled(exec_nested_level) && queryId != UINT64CONST(0))
{
/*
* Set up to track total elapsed time in ExecutorRun. Make sure the
@@ -1036,7 +1058,7 @@ pgss_ExecutorFinish(QueryDesc *queryDesc)
static void
pgss_ExecutorEnd(QueryDesc *queryDesc)
{
- uint64 queryId = queryDesc->plannedstmt->queryId;
+ uint64 queryId = get_query_label_hash(queryDesc->plannedstmt->queryIds, -1);
if (queryId != UINT64CONST(0) && queryDesc->totaltime &&
pgss_enabled(exec_nested_level))
@@ -1076,7 +1098,16 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
DestReceiver *dest, QueryCompletion *qc)
{
Node *parsetree = pstmt->utilityStmt;
- uint64 saved_queryId = pstmt->queryId;
+ QueryLabel *label = get_query_label(pstmt->queryIds, -1);
+ uint64 saved_queryId;
+
+ if (!label)
+ {
+ add_custom_query_label(&pstmt->queryIds, -1, UINT64CONST(0));
+ label = get_query_label(pstmt->queryIds, -1);
+ }
+
+ saved_queryId = label->hash;
/*
* Force utility statements to get queryId zero. We do this even in cases
@@ -1093,7 +1124,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* only.
*/
if (pgss_enabled(exec_nested_level) && pgss_track_utility)
- pstmt->queryId = UINT64CONST(0);
+ label->hash = UINT64CONST(0);
/*
* If it's an EXECUTE statement, we don't track it and don't increment the
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index b970997c34..b31a870156 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -166,7 +166,6 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
{
ExplainState *es = NewExplainState();
TupOutputState *tstate;
- JumbleState *jstate = NULL;
Query *query;
List *rewritten;
ListCell *lc;
@@ -246,10 +245,10 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
query = castNode(Query, stmt->query);
if (IsQueryIdEnabled())
- jstate = JumbleQuery(query, pstate->p_sourcetext);
+ GenerateQueryLabels(query, pstate->p_sourcetext);
if (post_parse_analyze_hook)
- (*post_parse_analyze_hook) (pstate, query, jstate);
+ (*post_parse_analyze_hook) (pstate, query);
/*
* Parse analysis was done already, but we still have to run the rule
@@ -604,14 +603,14 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
/* Create textual dump of plan tree */
ExplainPrintPlan(es, queryDesc);
- if (es->verbose && plannedstmt->queryId != UINT64CONST(0))
+ if (es->verbose && plannedstmt->queryIds != NIL)
{
/*
* Output the queryid as an int64 rather than a uint64 so we match
* what would be seen in the BIGINT pg_stat_statements.queryid column.
*/
- ExplainPropertyInteger("Query Identifier", NULL, (int64)
- plannedstmt->queryId, es);
+ ExplainPropertyInteger("Query Identifier", NULL,
+ get_query_label_hash(plannedstmt->queryIds, 0), es);
}
/* Show buffer usage in planning */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 549d9eb696..cbb97cf91f 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -62,6 +62,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
+#include "utils/queryjumble.h"
#include "utils/rls.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
@@ -135,7 +136,8 @@ ExecutorStart(QueryDesc *queryDesc, int eflags)
* that it's harmless to report the query_id multiple time, as the call
* will be ignored if the top level query_id has already been reported.
*/
- pgstat_report_query_id(queryDesc->plannedstmt->queryId, false);
+ pgstat_report_query_id(
+ get_query_label_hash(queryDesc->plannedstmt->queryIds, 0), false);
if (ExecutorStart_hook)
(*ExecutorStart_hook) (queryDesc, eflags);
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 5dd8ab7db2..d025860f45 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -49,6 +49,7 @@
#include "utils/dsa.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
+#include "utils/queryjumble.h"
#include "utils/snapmgr.h"
/*
@@ -175,7 +176,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
*/
pstmt = makeNode(PlannedStmt);
pstmt->commandType = CMD_SELECT;
- pstmt->queryId = pgstat_get_my_query_id();
+ add_custom_query_label(&pstmt->queryIds, 0, pgstat_get_my_query_id());
pstmt->hasReturning = false;
pstmt->hasModifyingCTE = false;
pstmt->canSetTag = true;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 90b5da51c9..6f159e0cbd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -87,7 +87,7 @@ _copyPlannedStmt(const PlannedStmt *from)
PlannedStmt *newnode = makeNode(PlannedStmt);
COPY_SCALAR_FIELD(commandType);
- COPY_SCALAR_FIELD(queryId);
+ COPY_NODE_FIELD(queryIds);
COPY_SCALAR_FIELD(hasReturning);
COPY_SCALAR_FIELD(hasModifyingCTE);
COPY_SCALAR_FIELD(canSetTag);
@@ -3159,6 +3159,20 @@ _copyTriggerTransition(const TriggerTransition *from)
return newnode;
}
+static QueryLabel *
+_copyQueryLabel(const QueryLabel *from)
+{
+ QueryLabel *newnode = makeNode(QueryLabel);
+
+ COPY_SCALAR_FIELD(kind);
+ COPY_SCALAR_FIELD(hash);
+
+ /* Caller should re-generate labels if it want to use it. */
+ newnode->context = NULL;
+
+ return newnode;
+}
+
static Query *
_copyQuery(const Query *from)
{
@@ -3166,7 +3180,7 @@ _copyQuery(const Query *from)
COPY_SCALAR_FIELD(commandType);
COPY_SCALAR_FIELD(querySource);
- COPY_SCALAR_FIELD(queryId);
+ COPY_NODE_FIELD(queryIds);
COPY_SCALAR_FIELD(canSetTag);
COPY_NODE_FIELD(utilityStmt);
COPY_SCALAR_FIELD(resultRelation);
@@ -5405,6 +5419,9 @@ copyObjectImpl(const void *from)
/*
* PARSE NODES
*/
+ case T_QueryLabel:
+ retval = _copyQueryLabel(from);
+ break;
case T_Query:
retval = _copyQuery(from);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2b0236937a..c86eb636b6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -305,7 +305,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_TYPE("PLANNEDSTMT");
WRITE_ENUM_FIELD(commandType, CmdType);
- WRITE_UINT64_FIELD(queryId);
+ WRITE_NODE_FIELD(queryIds);
WRITE_BOOL_FIELD(hasReturning);
WRITE_BOOL_FIELD(hasModifyingCTE);
WRITE_BOOL_FIELD(canSetTag);
@@ -3030,6 +3030,16 @@ _outStatsElem(StringInfo str, const StatsElem *node)
WRITE_NODE_FIELD(expr);
}
+/* Needed for dump of a PlannedStmt node */
+static void
+_outQueryLabel(StringInfo str, const QueryLabel *node)
+{
+ WRITE_NODE_TYPE("QUERYLABEL");
+
+ WRITE_INT_FIELD(kind);
+ WRITE_UINT64_FIELD(hash);
+}
+
static void
_outQuery(StringInfo str, const Query *node)
{
@@ -3037,7 +3047,7 @@ _outQuery(StringInfo str, const Query *node)
WRITE_ENUM_FIELD(commandType, CmdType);
WRITE_ENUM_FIELD(querySource, QuerySource);
- /* we intentionally do not print the queryId field */
+ /* we intentionally do not print the queryId fields */
WRITE_BOOL_FIELD(canSetTag);
/*
@@ -4403,6 +4413,9 @@ outNode(StringInfo str, const void *obj)
case T_StatsElem:
_outStatsElem(str, obj);
break;
+ case T_QueryLabel:
+ _outQueryLabel(str, obj);
+ break;
case T_Query:
_outQuery(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3f68f7c18d..47c35707d4 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -238,6 +238,17 @@ readBitmapset(void)
return _readBitmapset();
}
+static QueryLabel *
+_readQueryLabel(void)
+{
+ READ_LOCALS(QueryLabel);
+ READ_INT_FIELD(kind);
+ READ_UINT64_FIELD(hash);
+ local_node->context = NULL;
+
+ READ_DONE();
+}
+
/*
* _readQuery
*/
@@ -248,7 +259,7 @@ _readQuery(void)
READ_ENUM_FIELD(commandType, CmdType);
READ_ENUM_FIELD(querySource, QuerySource);
- local_node->queryId = UINT64CONST(0); /* not saved in output format */
+ local_node->queryIds = NIL; /* not saved in output format */
READ_BOOL_FIELD(canSetTag);
READ_NODE_FIELD(utilityStmt);
READ_INT_FIELD(resultRelation);
@@ -1578,7 +1589,7 @@ _readPlannedStmt(void)
READ_LOCALS(PlannedStmt);
READ_ENUM_FIELD(commandType, CmdType);
- READ_UINT64_FIELD(queryId);
+ READ_NODE_FIELD(queryIds);
READ_BOOL_FIELD(hasReturning);
READ_BOOL_FIELD(hasModifyingCTE);
READ_BOOL_FIELD(canSetTag);
@@ -2728,6 +2739,8 @@ parseNodeString(void)
if (MATCH("QUERY", 5))
return_value = _readQuery();
+ else if (MATCH("QUERYLABEL", 10))
+ return_value = _readQueryLabel();
else if (MATCH("WITHCHECKOPTION", 15))
return_value = _readWithCheckOption();
else if (MATCH("SORTGROUPCLAUSE", 15))
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index bd09f85aea..2ef8a97997 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -510,7 +510,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
result = makeNode(PlannedStmt);
result->commandType = parse->commandType;
- result->queryId = parse->queryId;
+ result->queryIds = list_copy(parse->queryIds);
result->hasReturning = (parse->returningList != NIL);
result->hasModifyingCTE = parse->hasModifyingCTE;
result->canSetTag = parse->canSetTag;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6ac2e9ce23..5925641795 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -113,7 +113,6 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
{
ParseState *pstate = make_parsestate(NULL);
Query *query;
- JumbleState *jstate = NULL;
Assert(sourceText != NULL); /* required as of 8.4 */
@@ -127,14 +126,15 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
query = transformTopLevelStmt(pstate, parseTree);
if (IsQueryIdEnabled())
- jstate = JumbleQuery(query, sourceText);
+ GenerateQueryLabels(query, sourceText);
if (post_parse_analyze_hook)
- (*post_parse_analyze_hook) (pstate, query, jstate);
+ (*post_parse_analyze_hook) (pstate, query);
free_parsestate(pstate);
- pgstat_report_query_id(query->queryId, false);
+ /* Report id of default generator. */
+ pgstat_report_query_id(get_query_label_hash(query->queryIds, 0), false);
return query;
}
@@ -152,7 +152,6 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
{
ParseState *pstate = make_parsestate(NULL);
Query *query;
- JumbleState *jstate = NULL;
Assert(sourceText != NULL); /* required as of 8.4 */
@@ -166,14 +165,14 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
check_variable_parameters(pstate, query);
if (IsQueryIdEnabled())
- jstate = JumbleQuery(query, sourceText);
+ GenerateQueryLabels(query, sourceText);
if (post_parse_analyze_hook)
- (*post_parse_analyze_hook) (pstate, query, jstate);
+ (*post_parse_analyze_hook) (pstate, query);
free_parsestate(pstate);
- pgstat_report_query_id(query->queryId, false);
+ pgstat_report_query_id(get_query_label_hash(query->queryIds, 0), false);
return query;
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3d82138cb3..1cce0949b9 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -4079,7 +4079,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
List *
QueryRewrite(Query *parsetree)
{
- uint64 input_query_id = parsetree->queryId;
+ List *input_query_ids = parsetree->queryIds;
List *querylist;
List *results;
ListCell *l;
@@ -4114,7 +4114,7 @@ QueryRewrite(Query *parsetree)
query = fireRIRrules(query, NIL);
- query->queryId = input_query_id;
+ query->queryIds = input_query_ids;
results = lappend(results, query);
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index fda2e9360e..76a8312b47 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -686,7 +686,6 @@ pg_analyze_and_rewrite_params(RawStmt *parsetree,
ParseState *pstate;
Query *query;
List *querytree_list;
- JumbleState *jstate = NULL;
Assert(query_string != NULL); /* required as of 8.4 */
@@ -706,14 +705,14 @@ pg_analyze_and_rewrite_params(RawStmt *parsetree,
query = transformTopLevelStmt(pstate, parsetree);
if (IsQueryIdEnabled())
- jstate = JumbleQuery(query, query_string);
+ GenerateQueryLabels(query, query_string);
if (post_parse_analyze_hook)
- (*post_parse_analyze_hook) (pstate, query, jstate);
+ (*post_parse_analyze_hook) (pstate, query);
free_parsestate(pstate);
- pgstat_report_query_id(query->queryId, false);
+ pgstat_report_query_id(get_query_label_hash(query->queryIds, 0), false);
if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS");
@@ -797,7 +796,7 @@ pg_rewrite_query(Query *query)
* queryId is not saved in stored rules, but we must preserve
* it here to avoid breaking pg_stat_statements.
*/
- new_query->queryId = query->queryId;
+ new_query->queryIds = list_copy(query->queryIds);
new_list = lappend(new_list, new_query);
pfree(str);
@@ -933,7 +932,7 @@ pg_plan_queries(List *querytrees, const char *query_string, int cursorOptions,
stmt->utilityStmt = query->utilityStmt;
stmt->stmt_location = query->stmt_location;
stmt->stmt_len = query->stmt_len;
- stmt->queryId = query->queryId;
+ stmt->queryIds = list_copy(query->queryIds);
}
else
{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4c94f09c64..fb5dd1cdce 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4695,7 +4695,7 @@ static struct config_enum ConfigureNamesEnum[] =
},
&compute_query_id,
COMPUTE_QUERY_ID_AUTO, compute_query_id_options,
- NULL, NULL, NULL
+ NULL, assign_query_id, NULL
},
{
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/utils/misc/queryjumble.c
index a67487e5fe..59bf53bc46 100644
--- a/src/backend/utils/misc/queryjumble.c
+++ b/src/backend/utils/misc/queryjumble.c
@@ -97,18 +97,18 @@ CleanQuerytext(const char *query, int *location, int *len)
return query;
}
-JumbleState *
-JumbleQuery(Query *query, const char *querytext)
+int64
+JumbleQuery(Query *query, const char *querytext, void **context)
{
- JumbleState *jstate = NULL;
+ JumbleState *jstate = NULL;
+ int64 queryId;
Assert(IsQueryIdEnabled());
if (query->utilityStmt)
{
- query->queryId = compute_utility_query_id(querytext,
- query->stmt_location,
- query->stmt_len);
+ queryId = compute_utility_query_id(querytext, query->stmt_location,
+ query->stmt_len);
}
else
{
@@ -125,19 +125,20 @@ JumbleQuery(Query *query, const char *querytext)
/* Compute query ID and mark the Query node with it */
JumbleQueryInternal(jstate, query);
- query->queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,
- jstate->jumble_len,
- 0));
+ queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,
+ jstate->jumble_len,
+ 0));
/*
* If we are unlucky enough to get a hash of zero, use 1 instead, to
* prevent confusion with the utility-statement case.
*/
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
+ if (queryId == UINT64CONST(0))
+ queryId = UINT64CONST(1);
}
- return jstate;
+ *context = jstate;
+ return queryId;
}
/*
@@ -150,7 +151,29 @@ void
EnableQueryId(void)
{
if (compute_query_id != COMPUTE_QUERY_ID_OFF)
+ {
+ (void) RegisterQueryIdGen(0, JumbleQuery);
query_id_enabled = true;
+ }
+}
+
+void
+assign_query_id(int newval, void *extra)
+{
+ switch (newval)
+ {
+ case COMPUTE_QUERY_ID_OFF:
+ /* De-register routine should be implemented */
+ break;
+ case COMPUTE_QUERY_ID_AUTO:
+ break;
+ case COMPUTE_QUERY_ID_ON:
+ (void) RegisterQueryIdGen(0, JumbleQuery);
+ break;
+ default:
+ elog(ERROR, "Unknown value");
+ break;
+ }
}
/*
@@ -856,3 +879,120 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++;
}
}
+
+/* *******************************************************************************
+ *
+ * Query label custom generation machinery.
+ *
+ * ******************************************************************************/
+
+#include "utils/hsearch.h"
+
+static HTAB *QueryLabelGens = NULL;
+
+typedef struct QLGeneratorEntry
+{
+ int16 kind;
+ query_generator_type callback;
+} QLGeneratorEntry;
+
+/*
+ * An internal function to register a new callback structure
+ * Use a positive value for the 'kind' field to register an query label generator.
+ * Caller should worry about intersections by this value with other extensions.
+ */
+bool
+RegisterQueryIdGen(const int16 kind, query_generator_type callback)
+{
+ QLGeneratorEntry *entry;
+ bool found;
+
+ if (QueryLabelGens == NULL)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = sizeof(int16);
+ ctl.entrysize = sizeof(QLGeneratorEntry);
+
+ QueryLabelGens = hash_create("Query Label Generators", 8, &ctl,
+ HASH_ELEM | HASH_BLOBS);
+ }
+
+ if (kind < 0)
+ elog(ERROR, "Can't use value %d as an query generator kind.", kind);
+
+ entry = (QLGeneratorEntry *) hash_search(QueryLabelGens, (void *) &kind,
+ HASH_ENTER, &found);
+ if (found)
+ /* Give caller a chance to process the problem. */
+ return false;
+
+ entry->callback = callback;
+ return true;
+}
+
+/*
+ * Create QueryLabel entry for each registered generator.
+ * All memory allocations is made in current memory context.
+ */
+void
+GenerateQueryLabels(Query *query, const char *querytext)
+{
+ HASH_SEQ_STATUS hash_seq;
+ QLGeneratorEntry *entry;
+
+ /* Newly generated query haven't any labels. */
+ Assert(query->queryIds == NIL);
+
+ if (QueryLabelGens == NULL || hash_get_num_entries(QueryLabelGens) == 0)
+ return;
+
+ hash_seq_init(&hash_seq, QueryLabelGens);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ QueryLabel *qlabel = makeNode(QueryLabel);
+
+ qlabel->kind = entry->kind;
+ qlabel->hash = entry->callback(query, querytext, &qlabel->context);
+ query->queryIds = lappend(query->queryIds, qlabel);
+ }
+}
+
+int64
+get_query_label_hash(List *queryIds, const int16 kind)
+{
+ QueryLabel *label;
+
+ label = get_query_label(queryIds, kind);
+ return (label) ? label->hash : 0;
+}
+
+QueryLabel *
+get_query_label(List *queryIds, const int16 kind)
+{
+ ListCell *lc;
+
+ foreach(lc, queryIds)
+ {
+ QueryLabel *label = lfirst_node(QueryLabel, lc);
+ if (label->kind == kind)
+ return label;
+ }
+ return NULL;
+}
+
+bool
+add_custom_query_label(List **queryIds, int16 kind, int64 hash)
+{
+ QueryLabel *label;
+
+ if (get_query_label_hash(*queryIds, kind) != 0)
+ elog(ERROR, "Duplicated custom label %ld for the kind %d.", hash, kind);
+
+ label = makeNode(QueryLabel);
+ label->kind = kind;
+ label->hash = hash;
+ label->context = NULL;
+ *queryIds = lappend(*queryIds, label);
+ return true;
+}
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f9ddafd345..0835034c2d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -314,6 +314,7 @@ typedef enum NodeTag
T_RawStmt,
T_Query,
T_PlannedStmt,
+ T_QueryLabel,
T_InsertStmt,
T_DeleteStmt,
T_UpdateStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3e9bdc781f..bc9ca32273 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -70,6 +70,14 @@ typedef enum SetQuantifier
SET_QUANTIFIER_DISTINCT
} SetQuantifier;
+typedef struct QueryLabel
+{
+ NodeTag type;
+ int16 kind; /* unique ID of generator. 0 reserved for in-core jumbling routine. */
+ int64 hash; /* generated stamp. */
+ void *context; /* internal generator-related data. */
+} QueryLabel;
+
/*
* Grantable rights are encoded so that we can OR them together in a bitmask.
* The present representation of AclItem limits us to 16 distinct rights,
@@ -121,7 +129,7 @@ typedef struct Query
QuerySource querySource; /* where did I come from? */
- uint64 queryId; /* query identifier (can be set by plugins) */
+ List *queryIds; /* query identifiers (can be added by plugins) */
bool canSetTag; /* do I set the command result tag? */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 0b518ce6b2..f588311de2 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -45,7 +45,7 @@ typedef struct PlannedStmt
CmdType commandType; /* select|insert|update|delete|utility */
- uint64 queryId; /* query identifier (copied from Query) */
+ List *queryIds; /* query identifiers (copied from Query) */
bool hasReturning; /* is it insert|update|delete RETURNING? */
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 0022184de0..455396c054 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -19,8 +19,7 @@
/* Hook for plugins to get control at end of parse analysis */
typedef void (*post_parse_analyze_hook_type) (ParseState *pstate,
- Query *query,
- JumbleState *jstate);
+ Query *query);
extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
diff --git a/src/include/utils/queryjumble.h b/src/include/utils/queryjumble.h
index a4c277269e..f09b88e3ec 100644
--- a/src/include/utils/queryjumble.h
+++ b/src/include/utils/queryjumble.h
@@ -65,7 +65,7 @@ extern int compute_query_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
-extern JumbleState *JumbleQuery(Query *query, const char *querytext);
+extern int64 JumbleQuery(Query *query, const char *querytext, void **context);
extern void EnableQueryId(void);
extern bool query_id_enabled;
@@ -84,4 +84,14 @@ IsQueryIdEnabled(void)
return query_id_enabled;
}
+typedef int64 (*query_generator_type) (Query *query, const char *querytext,
+ void **context);
+
+extern void assign_query_id(int newval, void *extra);
+extern bool RegisterQueryIdGen(const int16 kind, query_generator_type callback);
+extern void GenerateQueryLabels(Query *query, const char *querytext);
+extern int64 get_query_label_hash(List *queryIds, const int16 kind);
+extern QueryLabel *get_query_label(List *queryIds, const int16 kind);
+extern bool add_custom_query_label(List **queryIds, int16 kind, int64 hash);
+
#endif /* QUERYJUMBLE_H */
--
2.25.1
view thread (14+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: Multiple Query IDs for a rewritten parse tree
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox