summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/util/Makefile1
-rw-r--r--src/backend/optimizer/util/extendplan.c183
-rw-r--r--src/backend/optimizer/util/meson.build1
-rw-r--r--src/include/nodes/pathnodes.h12
-rw-r--r--src/include/optimizer/extendplan.h72
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