diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/optimizer/util/Makefile | 1 | ||||
-rw-r--r-- | src/backend/optimizer/util/extendplan.c | 183 | ||||
-rw-r--r-- | src/backend/optimizer/util/meson.build | 1 | ||||
-rw-r--r-- | src/include/nodes/pathnodes.h | 12 | ||||
-rw-r--r-- | src/include/optimizer/extendplan.h | 72 |
5 files changed, 269 insertions, 0 deletions
diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefile index 4fb115cb118..87b4c3c0869 100644 --- a/src/backend/optimizer/util/Makefile +++ b/src/backend/optimizer/util/Makefile @@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global OBJS = \ appendinfo.o \ clauses.o \ + extendplan.o \ inherit.o \ joininfo.o \ orclauses.o \ diff --git a/src/backend/optimizer/util/extendplan.c b/src/backend/optimizer/util/extendplan.c new file mode 100644 index 00000000000..03d32277ba1 --- /dev/null +++ b/src/backend/optimizer/util/extendplan.c @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------- + * + * extendplan.c + * Extend core planner objects with additional private state + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994-5, Regents of the University of California + * + * The interfaces defined in this file make it possible for loadable + * modules to store their own private state inside of key planner data + * structures -- specifically, the PlannerGlobal, PlannerInfo, and + * RelOptInfo structures. This can make it much easier to write + * reasonably efficient planner extensions; for instance, code that + * uses set_join_pathlist_hook can arrange to compute a key intermediate + * result once per joinrel rather than on every call. + * + * IDENTIFICATION + * src/backend/optimizer/util/extendplan.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "optimizer/extendplan.h" +#include "port/pg_bitutils.h" +#include "utils/memutils.h" + +static const char **PlannerExtensionNameArray = NULL; +static int PlannerExtensionNamesAssigned = 0; +static int PlannerExtensionNamesAllocated = 0; + +/* + * Map the name of a planner 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 the remaining functions in this + * file. + */ +int +GetPlannerExtensionId(const char *extension_name) +{ + /* Search for an existing extension by this name; if found, return ID. */ + for (int i = 0; i < PlannerExtensionNamesAssigned; ++i) + if (strcmp(PlannerExtensionNameArray[i], extension_name) == 0) + return i; + + /* If there is no array yet, create one. */ + if (PlannerExtensionNameArray == NULL) + { + PlannerExtensionNamesAllocated = 16; + PlannerExtensionNameArray = (const char **) + MemoryContextAlloc(TopMemoryContext, + PlannerExtensionNamesAllocated + * sizeof(char *)); + } + + /* If there's an array but it's currently full, expand it. */ + if (PlannerExtensionNamesAssigned >= PlannerExtensionNamesAllocated) + { + int i = pg_nextpower2_32(PlannerExtensionNamesAssigned + 1); + + PlannerExtensionNameArray = (const char **) + repalloc(PlannerExtensionNameArray, i * sizeof(char *)); + PlannerExtensionNamesAllocated = i; + } + + /* Assign and return new ID. */ + PlannerExtensionNameArray[PlannerExtensionNamesAssigned] = extension_name; + return PlannerExtensionNamesAssigned++; +} + +/* + * Store extension-specific state into a PlannerGlobal. + */ +void +SetPlannerGlobalExtensionState(PlannerGlobal *glob, int extension_id, + void *opaque) +{ + Assert(extension_id >= 0); + + /* If there is no array yet, create one. */ + if (glob->extension_state == NULL) + { + MemoryContext planner_cxt; + Size sz; + + planner_cxt = GetMemoryChunkContext(glob); + glob->extension_state_allocated = + Max(4, pg_nextpower2_32(extension_id + 1)); + sz = glob->extension_state_allocated * sizeof(void *); + glob->extension_state = MemoryContextAllocZero(planner_cxt, sz); + } + + /* If there's an array but it's currently full, expand it. */ + if (extension_id >= glob->extension_state_allocated) + { + int i; + + i = pg_nextpower2_32(extension_id + 1); + glob->extension_state = (void **) + repalloc0(glob->extension_state, + glob->extension_state_allocated * sizeof(void *), + i * sizeof(void *)); + glob->extension_state_allocated = i; + } + + glob->extension_state[extension_id] = opaque; +} + +/* + * Store extension-specific state into a PlannerInfo. + */ +void +SetPlannerInfoExtensionState(PlannerInfo *root, int extension_id, + void *opaque) +{ + Assert(extension_id >= 0); + + /* If there is no array yet, create one. */ + if (root->extension_state == NULL) + { + Size sz; + + root->extension_state_allocated = + Max(4, pg_nextpower2_32(extension_id + 1)); + sz = root->extension_state_allocated * sizeof(void *); + root->extension_state = MemoryContextAllocZero(root->planner_cxt, sz); + } + + /* If there's an array but it's currently full, expand it. */ + if (extension_id >= root->extension_state_allocated) + { + int i; + + i = pg_nextpower2_32(extension_id + 1); + root->extension_state = (void **) + repalloc0(root->extension_state, + root->extension_state_allocated * sizeof(void *), + i * sizeof(void *)); + root->extension_state_allocated = i; + } + + root->extension_state[extension_id] = opaque; +} + +/* + * Store extension-specific state into a RelOptInfo. + */ +void +SetRelOptInfoExtensionState(RelOptInfo *rel, int extension_id, + void *opaque) +{ + Assert(extension_id >= 0); + + /* If there is no array yet, create one. */ + if (rel->extension_state == NULL) + { + MemoryContext planner_cxt; + Size sz; + + planner_cxt = GetMemoryChunkContext(rel); + rel->extension_state_allocated = + Max(4, pg_nextpower2_32(extension_id + 1)); + sz = rel->extension_state_allocated * sizeof(void *); + rel->extension_state = MemoryContextAllocZero(planner_cxt, sz); + } + + /* If there's an array but it's currently full, expand it. */ + if (extension_id >= rel->extension_state_allocated) + { + int i; + + i = pg_nextpower2_32(extension_id + 1); + rel->extension_state = (void **) + repalloc0(rel->extension_state, + rel->extension_state_allocated * sizeof(void *), + i * sizeof(void *)); + rel->extension_state_allocated = i; + } + + rel->extension_state[extension_id] = opaque; +} diff --git a/src/backend/optimizer/util/meson.build b/src/backend/optimizer/util/meson.build index b3bf913d096..f71f56e37a1 100644 --- a/src/backend/optimizer/util/meson.build +++ b/src/backend/optimizer/util/meson.build @@ -3,6 +3,7 @@ backend_sources += files( 'appendinfo.c', 'clauses.c', + 'extendplan.c', 'inherit.c', 'joininfo.c', 'orclauses.c', diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 7ee9a7a68d8..554d7c3ef67 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -185,6 +185,10 @@ typedef struct PlannerGlobal /* hash table for NOT NULL attnums of relations */ struct HTAB *rel_notnullatts_hash pg_node_attr(read_write_ignore); + + /* extension state */ + void **extension_state pg_node_attr(read_write_ignore); + int extension_state_allocated; } PlannerGlobal; /* macro for fetching the Plan associated with a SubPlan node */ @@ -586,6 +590,10 @@ struct PlannerInfo /* PartitionPruneInfos added in this query's plan. */ List *partPruneInfos; + + /* extension state */ + void **extension_state pg_node_attr(read_write_ignore); + int extension_state_allocated; }; @@ -1097,6 +1105,10 @@ typedef struct RelOptInfo List **partexprs pg_node_attr(read_write_ignore); /* Nullable partition key expressions */ List **nullable_partexprs pg_node_attr(read_write_ignore); + + /* extension state */ + void **extension_state pg_node_attr(read_write_ignore); + int extension_state_allocated; } RelOptInfo; /* diff --git a/src/include/optimizer/extendplan.h b/src/include/optimizer/extendplan.h new file mode 100644 index 00000000000..de9618761dd --- /dev/null +++ b/src/include/optimizer/extendplan.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * extendplan.h + * Extend core planner objects with additional private state + * + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/optimizer/extendplan.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXTENDPLAN_H +#define EXTENDPLAN_H + +#include "nodes/pathnodes.h" + +extern int GetPlannerExtensionId(const char *extension_name); + +/* + * Get extension-specific state from a PlannerGlobal. + */ +static inline void * +GetPlannerGlobalExtensionState(PlannerGlobal *glob, int extension_id) +{ + Assert(extension_id >= 0); + + if (extension_id >= glob->extension_state_allocated) + return NULL; + + return glob->extension_state[extension_id]; +} + +/* + * Get extension-specific state from a PlannerInfo. + */ +static inline void * +GetPlannerInfoExtensionState(PlannerInfo *root, int extension_id) +{ + Assert(extension_id >= 0); + + if (extension_id >= root->extension_state_allocated) + return NULL; + + return root->extension_state[extension_id]; +} + +/* + * Get extension-specific state from a PlannerInfo. + */ +static inline void * +GetRelOptInfoExtensionState(RelOptInfo *rel, int extension_id) +{ + Assert(extension_id >= 0); + + if (extension_id >= rel->extension_state_allocated) + return NULL; + + return rel->extension_state[extension_id]; +} + +/* Functions to store private state into various planner objects */ +extern void SetPlannerGlobalExtensionState(PlannerGlobal *glob, + int extension_id, + void *opaque); +extern void SetPlannerInfoExtensionState(PlannerInfo *root, int extension_id, + void *opaque); +extern void SetRelOptInfoExtensionState(RelOptInfo *rel, int extension_id, + void *opaque); + +#endif |