diff options
-rw-r--r-- | contrib/auto_explain/auto_explain.c | 1 | ||||
-rw-r--r-- | contrib/file_fdw/file_fdw.c | 3 | ||||
-rw-r--r-- | contrib/postgres_fdw/postgres_fdw.c | 2 | ||||
-rw-r--r-- | src/backend/commands/Makefile | 1 | ||||
-rw-r--r-- | src/backend/commands/createas.c | 2 | ||||
-rw-r--r-- | src/backend/commands/explain.c | 143 | ||||
-rw-r--r-- | src/backend/commands/explain_dr.c | 1 | ||||
-rw-r--r-- | src/backend/commands/explain_format.c | 1 | ||||
-rw-r--r-- | src/backend/commands/explain_state.c | 371 | ||||
-rw-r--r-- | src/backend/commands/meson.build | 1 | ||||
-rw-r--r-- | src/backend/commands/prepare.c | 2 | ||||
-rw-r--r-- | src/backend/executor/execAmi.c | 1 | ||||
-rw-r--r-- | src/backend/tcop/pquery.c | 1 | ||||
-rw-r--r-- | src/include/commands/explain.h | 80 | ||||
-rw-r--r-- | src/include/commands/explain_state.h | 95 | ||||
-rw-r--r-- | src/include/commands/prepare.h | 3 | ||||
-rw-r--r-- | src/include/nodes/extensible.h | 2 | ||||
-rw-r--r-- | src/tools/pgindent/typedefs.list | 2 |
18 files changed, 503 insertions, 209 deletions
diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 7007a226c08..3b73bd19107 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -17,6 +17,7 @@ #include "access/parallel.h" #include "commands/explain.h" #include "commands/explain_format.h" +#include "commands/explain_state.h" #include "common/pg_prng.h" #include "executor/instrument.h" #include "utils/guc.h" diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index bf707c812ed..56ececac70b 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -24,9 +24,10 @@ #include "commands/copy.h" #include "commands/copyfrom_internal.h" #include "commands/defrem.h" -#include "commands/explain.h" #include "commands/explain_format.h" +#include "commands/explain_state.h" #include "commands/vacuum.h" +#include "executor/executor.h" #include "foreign/fdwapi.h" #include "foreign/foreign.h" #include "miscadmin.h" diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 1131a8bf77e..263c879026e 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -19,8 +19,8 @@ #include "access/table.h" #include "catalog/pg_opfamily.h" #include "commands/defrem.h" -#include "commands/explain.h" #include "commands/explain_format.h" +#include "commands/explain_state.h" #include "executor/execAsync.h" #include "foreign/fdwapi.h" #include "funcapi.h" diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 85cfea6fd71..cb2fbdc7c60 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -36,6 +36,7 @@ OBJS = \ explain.o \ explain_dr.o \ explain_format.o \ + explain_state.o \ extension.o \ foreigncmds.o \ functioncmds.o \ diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index 44b4665ccd3..0a4155773eb 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -35,6 +35,8 @@ #include "commands/prepare.h" #include "commands/tablecmds.h" #include "commands/view.h" +#include "executor/execdesc.h" +#include "executor/executor.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/queryjumble.h" diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 19ffcc2cacb..ab3898ff1eb 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -17,8 +17,10 @@ #include "catalog/pg_type.h" #include "commands/createas.h" #include "commands/defrem.h" +#include "commands/explain.h" #include "commands/explain_dr.h" #include "commands/explain_format.h" +#include "commands/explain_state.h" #include "commands/prepare.h" #include "foreign/fdwapi.h" #include "jit/jit.h" @@ -176,130 +178,11 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, JumbleState *jstate = NULL; Query *query; List *rewritten; - ListCell *lc; - bool timing_set = false; - bool buffers_set = false; - bool summary_set = false; - - /* Parse options list. */ - foreach(lc, stmt->options) - { - DefElem *opt = (DefElem *) lfirst(lc); - if (strcmp(opt->defname, "analyze") == 0) - es->analyze = defGetBoolean(opt); - else if (strcmp(opt->defname, "verbose") == 0) - es->verbose = defGetBoolean(opt); - else if (strcmp(opt->defname, "costs") == 0) - es->costs = defGetBoolean(opt); - else if (strcmp(opt->defname, "buffers") == 0) - { - buffers_set = true; - es->buffers = defGetBoolean(opt); - } - else if (strcmp(opt->defname, "wal") == 0) - es->wal = defGetBoolean(opt); - else if (strcmp(opt->defname, "settings") == 0) - es->settings = defGetBoolean(opt); - else if (strcmp(opt->defname, "generic_plan") == 0) - es->generic = defGetBoolean(opt); - else if (strcmp(opt->defname, "timing") == 0) - { - timing_set = true; - es->timing = defGetBoolean(opt); - } - else if (strcmp(opt->defname, "summary") == 0) - { - summary_set = true; - es->summary = defGetBoolean(opt); - } - else if (strcmp(opt->defname, "memory") == 0) - es->memory = defGetBoolean(opt); - else if (strcmp(opt->defname, "serialize") == 0) - { - if (opt->arg) - { - char *p = defGetString(opt); - - if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0) - es->serialize = EXPLAIN_SERIALIZE_NONE; - else if (strcmp(p, "text") == 0) - es->serialize = EXPLAIN_SERIALIZE_TEXT; - else if (strcmp(p, "binary") == 0) - es->serialize = EXPLAIN_SERIALIZE_BINARY; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"", - opt->defname, p), - parser_errposition(pstate, opt->location))); - } - else - { - /* SERIALIZE without an argument is taken as 'text' */ - es->serialize = EXPLAIN_SERIALIZE_TEXT; - } - } - else if (strcmp(opt->defname, "format") == 0) - { - char *p = defGetString(opt); - - if (strcmp(p, "text") == 0) - es->format = EXPLAIN_FORMAT_TEXT; - else if (strcmp(p, "xml") == 0) - es->format = EXPLAIN_FORMAT_XML; - else if (strcmp(p, "json") == 0) - es->format = EXPLAIN_FORMAT_JSON; - else if (strcmp(p, "yaml") == 0) - es->format = EXPLAIN_FORMAT_YAML; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"", - opt->defname, p), - parser_errposition(pstate, opt->location))); - } - else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unrecognized EXPLAIN option \"%s\"", - opt->defname), - parser_errposition(pstate, opt->location))); - } - - /* check that WAL is used with EXPLAIN ANALYZE */ - if (es->wal && !es->analyze) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("EXPLAIN option %s requires ANALYZE", "WAL"))); - - /* if the timing was not set explicitly, set default value */ - es->timing = (timing_set) ? es->timing : es->analyze; - - /* if the buffers was not set explicitly, set default value */ - es->buffers = (buffers_set) ? es->buffers : es->analyze; - - /* check that timing is used with EXPLAIN ANALYZE */ - if (es->timing && !es->analyze) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("EXPLAIN option %s requires ANALYZE", "TIMING"))); - - /* check that serialize is used with EXPLAIN ANALYZE */ - if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("EXPLAIN option %s requires ANALYZE", "SERIALIZE"))); - - /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */ - if (es->generic && es->analyze) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("EXPLAIN options ANALYZE and GENERIC_PLAN cannot be used together"))); - - /* if the summary was not set explicitly, set default value */ - es->summary = (summary_set) ? es->summary : es->analyze; + /* Configure the ExplainState based on the provided options */ + ParseExplainOptionList(es, stmt->options, pstate); + /* Extract the query and, if enabled, jumble it */ query = castNode(Query, stmt->query); if (IsQueryIdEnabled()) jstate = JumbleQuery(query); @@ -361,22 +244,6 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, } /* - * Create a new ExplainState struct initialized with default options. - */ -ExplainState * -NewExplainState(void) -{ - ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState)); - - /* Set default options (most fields can be left as zeroes). */ - es->costs = true; - /* Prepare output buffer. */ - es->str = makeStringInfo(); - - return es; -} - -/* * ExplainResultDesc - * construct the result tupledesc for an EXPLAIN */ diff --git a/src/backend/commands/explain_dr.c b/src/backend/commands/explain_dr.c index fb42bee6e72..5715546cf43 100644 --- a/src/backend/commands/explain_dr.c +++ b/src/backend/commands/explain_dr.c @@ -15,6 +15,7 @@ #include "commands/explain.h" #include "commands/explain_dr.h" +#include "commands/explain_state.h" #include "libpq/pqformat.h" #include "libpq/protocol.h" #include "utils/lsyscache.h" diff --git a/src/backend/commands/explain_format.c b/src/backend/commands/explain_format.c index bccdd76a874..752691d56db 100644 --- a/src/backend/commands/explain_format.c +++ b/src/backend/commands/explain_format.c @@ -15,6 +15,7 @@ #include "commands/explain.h" #include "commands/explain_format.h" +#include "commands/explain_state.h" #include "utils/json.h" #include "utils/xml.h" diff --git a/src/backend/commands/explain_state.c b/src/backend/commands/explain_state.c new file mode 100644 index 00000000000..1d4be3c18ac --- /dev/null +++ b/src/backend/commands/explain_state.c @@ -0,0 +1,371 @@ +/*------------------------------------------------------------------------- + * + * explain_state.c + * Code for initializing and accessing ExplainState objects + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994-5, Regents of the University of California + * + * In-core options have hard-coded fields inside ExplainState; e.g. if + * the user writes EXPLAIN (BUFFERS) then ExplainState's "buffers" member + * will be set to true. Extensions can also register options using + * RegisterExtensionExplainOption; so that e.g. EXPLAIN (BICYCLE 'red') + * will invoke a designated handler that knows what the legal values are + * for the BICYCLE option. However, it's not enough for an extension to be + * able to parse new options: it also needs a place to store the results + * of that parsing, and an ExplainState has no 'bicycle' field. + * + * To solve this problem, an ExplainState can contain an array of opaque + * pointers, one per extension. An extension can use GetExplainExtensionId + * to acquire an integer ID to acquire an offset into this array that is + * reserved for its exclusive use, and then use GetExplainExtensionState + * and SetExplainExtensionState to read and write its own private state + * within an ExplainState. + * + * Note that there is no requirement that the name of the option match + * the name of the extension; e.g. a pg_explain_conveyance extension could + * implement options for BICYCLE, MONORAIL, etc. + * + * IDENTIFICATION + * src/backend/commands/explain_state.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "commands/defrem.h" +#include "commands/explain.h" +#include "commands/explain_state.h" + +typedef struct +{ + const char *option_name; + ExplainOptionHandler option_handler; +} ExplainExtensionOption; + +static const char **ExplainExtensionNameArray = NULL; +static int ExplainExtensionNamesAssigned = 0; +static int ExplainExtensionNamesAllocated = 0; + +static ExplainExtensionOption *ExplainExtensionOptionArray = NULL; +static int ExplainExtensionOptionsAssigned = 0; +static int ExplainExtensionOptionsAllocated = 0; + +/* + * Create a new ExplainState struct initialized with default options. + */ +ExplainState * +NewExplainState(void) +{ + ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState)); + + /* Set default options (most fields can be left as zeroes). */ + es->costs = true; + /* Prepare output buffer. */ + es->str = makeStringInfo(); + + return es; +} + +/* + * Parse a list of EXPLAIN options and update an ExplainState accordingly. + */ +void +ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate) +{ + ListCell *lc; + bool timing_set = false; + bool buffers_set = false; + bool summary_set = false; + + /* Parse options list. */ + foreach(lc, options) + { + DefElem *opt = (DefElem *) lfirst(lc); + + if (strcmp(opt->defname, "analyze") == 0) + es->analyze = defGetBoolean(opt); + else if (strcmp(opt->defname, "verbose") == 0) + es->verbose = defGetBoolean(opt); + else if (strcmp(opt->defname, "costs") == 0) + es->costs = defGetBoolean(opt); + else if (strcmp(opt->defname, "buffers") == 0) + { + buffers_set = true; + es->buffers = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "wal") == 0) + es->wal = defGetBoolean(opt); + else if (strcmp(opt->defname, "settings") == 0) + es->settings = defGetBoolean(opt); + else if (strcmp(opt->defname, "generic_plan") == 0) + es->generic = defGetBoolean(opt); + else if (strcmp(opt->defname, "timing") == 0) + { + timing_set = true; + es->timing = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "summary") == 0) + { + summary_set = true; + es->summary = defGetBoolean(opt); + } + else if (strcmp(opt->defname, "memory") == 0) + es->memory = defGetBoolean(opt); + else if (strcmp(opt->defname, "serialize") == 0) + { + if (opt->arg) + { + char *p = defGetString(opt); + + if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0) + es->serialize = EXPLAIN_SERIALIZE_NONE; + else if (strcmp(p, "text") == 0) + es->serialize = EXPLAIN_SERIALIZE_TEXT; + else if (strcmp(p, "binary") == 0) + es->serialize = EXPLAIN_SERIALIZE_BINARY; + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"", + opt->defname, p), + parser_errposition(pstate, opt->location))); + } + else + { + /* SERIALIZE without an argument is taken as 'text' */ + es->serialize = EXPLAIN_SERIALIZE_TEXT; + } + } + else if (strcmp(opt->defname, "format") == 0) + { + char *p = defGetString(opt); + + if (strcmp(p, "text") == 0) + es->format = EXPLAIN_FORMAT_TEXT; + else if (strcmp(p, "xml") == 0) + es->format = EXPLAIN_FORMAT_XML; + else if (strcmp(p, "json") == 0) + es->format = EXPLAIN_FORMAT_JSON; + else if (strcmp(p, "yaml") == 0) + es->format = EXPLAIN_FORMAT_YAML; + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"", + opt->defname, p), + parser_errposition(pstate, opt->location))); + } + else if (!ApplyExtensionExplainOption(es, opt, pstate)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized EXPLAIN option \"%s\"", + opt->defname), + parser_errposition(pstate, opt->location))); + } + + /* check that WAL is used with EXPLAIN ANALYZE */ + if (es->wal && !es->analyze) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("EXPLAIN option %s requires ANALYZE", "WAL"))); + + /* if the timing was not set explicitly, set default value */ + es->timing = (timing_set) ? es->timing : es->analyze; + + /* if the buffers was not set explicitly, set default value */ + es->buffers = (buffers_set) ? es->buffers : es->analyze; + + /* check that timing is used with EXPLAIN ANALYZE */ + if (es->timing && !es->analyze) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("EXPLAIN option %s requires ANALYZE", "TIMING"))); + + /* check that serialize is used with EXPLAIN ANALYZE */ + if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("EXPLAIN option %s requires ANALYZE", "SERIALIZE"))); + + /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */ + if (es->generic && es->analyze) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("EXPLAIN options ANALYZE and GENERIC_PLAN cannot be used together"))); + + /* if the summary was not set explicitly, set default value */ + es->summary = (summary_set) ? es->summary : es->analyze; +} + +/* + * Map the name of an EXPLAIN extension to an integer ID. + * + * Within the lifetime of a particular backend, the same name will be mapped + * to the same ID every time. IDs are not stable across backends. Use the ID + * that you get from this function to call GetExplainExtensionState and + * SetExplainExtensionState. + * + * extension_name is assumed to be a constant string or allocated in storage + * that will never be freed. + */ +int +GetExplainExtensionId(const char *extension_name) +{ + /* Search for an existing extension by this name; if found, return ID. */ + for (int i = 0; i < ExplainExtensionNamesAssigned; ++i) + if (strcmp(ExplainExtensionNameArray[i], extension_name) == 0) + return i; + + /* If there is no array yet, create one. */ + if (ExplainExtensionNameArray == NULL) + { + ExplainExtensionNamesAllocated = 16; + ExplainExtensionNameArray = (const char **) + MemoryContextAlloc(TopMemoryContext, + ExplainExtensionNamesAllocated + * sizeof(char *)); + } + + /* If there's an array but it's currently full, expand it. */ + if (ExplainExtensionNamesAssigned >= ExplainExtensionNamesAllocated) + { + int i = pg_nextpower2_32(ExplainExtensionNamesAssigned + 1); + + ExplainExtensionNameArray = (const char **) + repalloc(ExplainExtensionNameArray, i * sizeof(char *)); + ExplainExtensionNamesAllocated = i; + } + + /* Assign and return new ID. */ + ExplainExtensionNameArray[ExplainExtensionNamesAssigned] = extension_name; + return ExplainExtensionNamesAssigned++; +} + +/* + * Get extension-specific state from an ExplainState. + * + * See comments for SetExplainExtensionState, below. + */ +void * +GetExplainExtensionState(ExplainState *es, int extension_id) +{ + Assert(extension_id >= 0); + + if (extension_id >= es->extension_state_allocated) + return NULL; + + return es->extension_state[extension_id]; +} + +/* + * Store extension-specific state into an ExplainState. + * + * To use this function, first obtain an integer extension_id using + * GetExplainExtensionId. Then use this function to store an opaque pointer + * in the ExplainState. Later, you can retrieve the opaque pointer using + * GetExplainExtensionState. + */ +void +SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque) +{ + Assert(extension_id >= 0); + + /* If there is no array yet, create one. */ + if (es->extension_state == NULL) + { + es->extension_state_allocated = 16; + es->extension_state = + palloc0(es->extension_state_allocated * sizeof(void *)); + } + + /* If there's an array but it's currently full, expand it. */ + if (extension_id >= es->extension_state_allocated) + { + int i; + + i = pg_nextpower2_32(es->extension_state_allocated + 1); + es->extension_state = (void **) + repalloc0(es->extension_state, + es->extension_state_allocated * sizeof(void *), + i * sizeof(void *)); + es->extension_state_allocated = i; + } + + es->extension_state[extension_id] = opaque; +} + +/* + * Register a new EXPLAIN option. + * + * When option_name is used as an EXPLAIN option, handler will be called and + * should update the ExplainState passed to it. See comments at top of file + * for a more detailed explanation. + * + * option_name is assumed to be a constant string or allocated in storage + * that will never be freed. + */ +void +RegisterExtensionExplainOption(const char *option_name, + ExplainOptionHandler handler) +{ + ExplainExtensionOption *exopt; + + /* Search for an existing option by this name; if found, update handler. */ + for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i) + { + if (strcmp(ExplainExtensionOptionArray[i].option_name, + option_name) == 0) + { + ExplainExtensionOptionArray[i].option_handler = handler; + return; + } + } + + /* If there is no array yet, create one. */ + if (ExplainExtensionOptionArray == NULL) + { + ExplainExtensionOptionsAllocated = 16; + ExplainExtensionOptionArray = (ExplainExtensionOption *) + MemoryContextAlloc(TopMemoryContext, + ExplainExtensionOptionsAllocated + * sizeof(char *)); + } + + /* If there's an array but it's currently full, expand it. */ + if (ExplainExtensionOptionsAssigned >= ExplainExtensionOptionsAllocated) + { + int i = pg_nextpower2_32(ExplainExtensionOptionsAssigned + 1); + + ExplainExtensionOptionArray = (ExplainExtensionOption *) + repalloc(ExplainExtensionOptionArray, i * sizeof(char *)); + ExplainExtensionOptionsAllocated = i; + } + + /* Assign and return new ID. */ + exopt = &ExplainExtensionOptionArray[ExplainExtensionOptionsAssigned++]; + exopt->option_name = option_name; + exopt->option_handler = handler; +} + +/* + * Apply an EXPLAIN option registered by an extension. + * + * If no extension has registered the named option, returns false. Otherwise, + * calls the appropriate handler function and then returns true. + */ +bool +ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, ParseState *pstate) +{ + for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i) + { + if (strcmp(ExplainExtensionOptionArray[i].option_name, + opt->defname) == 0) + { + ExplainExtensionOptionArray[i].option_handler(es, opt, pstate); + return true; + } + } + + return false; +} diff --git a/src/backend/commands/meson.build b/src/backend/commands/meson.build index ce8d1ab8bac..dd4cde41d32 100644 --- a/src/backend/commands/meson.build +++ b/src/backend/commands/meson.build @@ -24,6 +24,7 @@ backend_sources += files( 'explain.c', 'explain_dr.c', 'explain_format.c', + 'explain_state.c', 'extension.c', 'foreigncmds.c', 'functioncmds.c', diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 4d68d4d25c7..bf7d2b2309f 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -21,7 +21,9 @@ #include "access/xact.h" #include "catalog/pg_type.h" #include "commands/createas.h" +#include "commands/explain.h" #include "commands/explain_format.h" +#include "commands/explain_state.h" #include "commands/prepare.h" #include "funcapi.h" #include "nodes/nodeFuncs.h" diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index db525194554..1d0e8ad57b4 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -15,6 +15,7 @@ #include "access/amapi.h" #include "access/htup_details.h" #include "catalog/pg_class.h" +#include "executor/executor.h" #include "executor/nodeAgg.h" #include "executor/nodeAppend.h" #include "executor/nodeBitmapAnd.h" diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index dea24453a6c..8164d0fbb4f 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -20,6 +20,7 @@ #include "access/xact.h" #include "commands/prepare.h" #include "executor/execdesc.h" +#include "executor/executor.h" #include "executor/tstoreReceiver.h" #include "miscadmin.h" #include "pg_trace.h" diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index 64547bd9b9c..783f67b468a 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -14,70 +14,15 @@ #define EXPLAIN_H #include "executor/executor.h" -#include "lib/stringinfo.h" #include "parser/parse_node.h" -typedef enum ExplainSerializeOption -{ - EXPLAIN_SERIALIZE_NONE, - EXPLAIN_SERIALIZE_TEXT, - EXPLAIN_SERIALIZE_BINARY, -} ExplainSerializeOption; - -typedef enum ExplainFormat -{ - EXPLAIN_FORMAT_TEXT, - EXPLAIN_FORMAT_XML, - EXPLAIN_FORMAT_JSON, - EXPLAIN_FORMAT_YAML, -} ExplainFormat; - -typedef struct ExplainWorkersState -{ - int num_workers; /* # of worker processes the plan used */ - bool *worker_inited; /* per-worker state-initialized flags */ - StringInfoData *worker_str; /* per-worker transient output buffers */ - int *worker_state_save; /* per-worker grouping state save areas */ - StringInfo prev_str; /* saved output buffer while redirecting */ -} ExplainWorkersState; - -typedef struct ExplainState -{ - StringInfo str; /* output buffer */ - /* options */ - bool verbose; /* be verbose */ - bool analyze; /* print actual times */ - bool costs; /* print estimated costs */ - bool buffers; /* print buffer usage */ - bool wal; /* print WAL usage */ - bool timing; /* print detailed node timing */ - bool summary; /* print total planning and execution timing */ - bool memory; /* print planner's memory usage information */ - bool settings; /* print modified settings */ - bool generic; /* generate a generic plan */ - ExplainSerializeOption serialize; /* serialize the query's output? */ - ExplainFormat format; /* output format */ - /* state for output formatting --- not reset for each new plan tree */ - int indent; /* current indentation level */ - List *grouping_stack; /* format-specific grouping state */ - /* state related to the current plan tree (filled by ExplainPrintPlan) */ - PlannedStmt *pstmt; /* top of plan */ - List *rtable; /* range table */ - List *rtable_names; /* alias names for RTEs */ - List *deparse_cxt; /* context list for deparsing expressions */ - Bitmapset *printed_subplans; /* ids of SubPlans we've printed */ - bool hide_workers; /* set if we find an invisible Gather */ - int rtable_size; /* length of rtable excluding the RTE_GROUP - * entry */ - /* state related to the current plan node */ - ExplainWorkersState *workers_state; /* needed if parallel plan */ -} ExplainState; +struct ExplainState; /* defined in explain_state.h */ /* Hook for plugins to get control in ExplainOneQuery() */ typedef void (*ExplainOneQuery_hook_type) (Query *query, int cursorOptions, IntoClause *into, - ExplainState *es, + struct ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv); @@ -91,33 +36,34 @@ extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook; extern void ExplainQuery(ParseState *pstate, ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest); extern void standard_ExplainOneQuery(Query *query, int cursorOptions, - IntoClause *into, ExplainState *es, + IntoClause *into, struct ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv); -extern ExplainState *NewExplainState(void); - extern TupleDesc ExplainResultDesc(ExplainStmt *stmt); extern void ExplainOneUtility(Node *utilityStmt, IntoClause *into, - ExplainState *es, ParseState *pstate, + struct ExplainState *es, ParseState *pstate, ParamListInfo params); extern void ExplainOnePlan(PlannedStmt *plannedstmt, CachedPlan *cplan, CachedPlanSource *plansource, int plan_index, - IntoClause *into, ExplainState *es, + IntoClause *into, struct ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage, const MemoryContextCounters *mem_counters); -extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc); -extern void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc); +extern void ExplainPrintPlan(struct ExplainState *es, QueryDesc *queryDesc); +extern void ExplainPrintTriggers(struct ExplainState *es, + QueryDesc *queryDesc); -extern void ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc); +extern void ExplainPrintJITSummary(struct ExplainState *es, + QueryDesc *queryDesc); -extern void ExplainQueryText(ExplainState *es, QueryDesc *queryDesc); -extern void ExplainQueryParameters(ExplainState *es, ParamListInfo params, int maxlen); +extern void ExplainQueryText(struct ExplainState *es, QueryDesc *queryDesc); +extern void ExplainQueryParameters(struct ExplainState *es, + ParamListInfo params, int maxlen); #endif /* EXPLAIN_H */ diff --git a/src/include/commands/explain_state.h b/src/include/commands/explain_state.h new file mode 100644 index 00000000000..925097492b9 --- /dev/null +++ b/src/include/commands/explain_state.h @@ -0,0 +1,95 @@ +/*------------------------------------------------------------------------- + * + * explain_state.h + * prototypes for explain_state.c + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994-5, Regents of the University of California + * + * src/include/commands/explain_state.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXPLAIN_STATE_H +#define EXPLAIN_STATE_H + +#include "nodes/parsenodes.h" +#include "nodes/plannodes.h" +#include "parser/parse_node.h" + +typedef enum ExplainSerializeOption +{ + EXPLAIN_SERIALIZE_NONE, + EXPLAIN_SERIALIZE_TEXT, + EXPLAIN_SERIALIZE_BINARY, +} ExplainSerializeOption; + +typedef enum ExplainFormat +{ + EXPLAIN_FORMAT_TEXT, + EXPLAIN_FORMAT_XML, + EXPLAIN_FORMAT_JSON, + EXPLAIN_FORMAT_YAML, +} ExplainFormat; + +typedef struct ExplainWorkersState +{ + int num_workers; /* # of worker processes the plan used */ + bool *worker_inited; /* per-worker state-initialized flags */ + StringInfoData *worker_str; /* per-worker transient output buffers */ + int *worker_state_save; /* per-worker grouping state save areas */ + StringInfo prev_str; /* saved output buffer while redirecting */ +} ExplainWorkersState; + +typedef struct ExplainState +{ + StringInfo str; /* output buffer */ + /* options */ + bool verbose; /* be verbose */ + bool analyze; /* print actual times */ + bool costs; /* print estimated costs */ + bool buffers; /* print buffer usage */ + bool wal; /* print WAL usage */ + bool timing; /* print detailed node timing */ + bool summary; /* print total planning and execution timing */ + bool memory; /* print planner's memory usage information */ + bool settings; /* print modified settings */ + bool generic; /* generate a generic plan */ + ExplainSerializeOption serialize; /* serialize the query's output? */ + ExplainFormat format; /* output format */ + /* state for output formatting --- not reset for each new plan tree */ + int indent; /* current indentation level */ + List *grouping_stack; /* format-specific grouping state */ + /* state related to the current plan tree (filled by ExplainPrintPlan) */ + PlannedStmt *pstmt; /* top of plan */ + List *rtable; /* range table */ + List *rtable_names; /* alias names for RTEs */ + List *deparse_cxt; /* context list for deparsing expressions */ + Bitmapset *printed_subplans; /* ids of SubPlans we've printed */ + bool hide_workers; /* set if we find an invisible Gather */ + int rtable_size; /* length of rtable excluding the RTE_GROUP + * entry */ + /* state related to the current plan node */ + ExplainWorkersState *workers_state; /* needed if parallel plan */ + /* extensions */ + void **extension_state; + int extension_state_allocated; +} ExplainState; + +typedef void (*ExplainOptionHandler) (ExplainState *, DefElem *, ParseState *); + +extern ExplainState *NewExplainState(void); +extern void ParseExplainOptionList(ExplainState *es, List *options, + ParseState *pstate); + +extern int GetExplainExtensionId(const char *extension_name); +extern void *GetExplainExtensionState(ExplainState *es, int extension_id); +extern void SetExplainExtensionState(ExplainState *es, int extension_id, + void *opaque); + +extern void RegisterExtensionExplainOption(const char *option_name, + ExplainOptionHandler handler); +extern bool ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, + ParseState *pstate); + +#endif /* EXPLAIN_STATE_H */ diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h index b9533f1af84..08daac8c926 100644 --- a/src/include/commands/prepare.h +++ b/src/include/commands/prepare.h @@ -13,8 +13,9 @@ #ifndef PREPARE_H #define PREPARE_H -#include "commands/explain.h" +#include "commands/explain_state.h" #include "datatype/timestamp.h" +#include "tcop/dest.h" #include "utils/plancache.h" /* diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h index 552ce19bdd5..1129c4ba4b1 100644 --- a/src/include/nodes/extensible.h +++ b/src/include/nodes/extensible.h @@ -15,7 +15,7 @@ #define EXTENSIBLE_H #include "access/parallel.h" -#include "commands/explain.h" +#include "commands/explain_state.h" #include "nodes/execnodes.h" #include "nodes/pathnodes.h" #include "nodes/plannodes.h" diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index f4261145353..c04a47cf222 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -4271,3 +4271,5 @@ yyscan_t z_stream z_streamp zic_t +ExplainExtensionOption +ExplainOptionHandler |