--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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