Skip to content

Commit f6a2e60

Browse files
robertmhaasCommitfest Bot
authored andcommitted
Allow private state in certain planner data structures.
Extension that make extensive use of planner hooks may want to coordinate their efforts, for example to avoid duplicate computation, but that's currently difficult because there's no really good way to pass data between different hooks. To make that easier, allow for storage of extension-managed private state in PlannerGlobal, PlannerInfo, and RelOptInfo, along very similar lines to what we have permitted for ExplainState since commit c65bc2e.
1 parent 5487058 commit f6a2e60

File tree

5 files changed

+267
-0
lines changed

5 files changed

+267
-0
lines changed

src/backend/optimizer/util/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
1414

1515
OBJS = \
1616
appendinfo.o \
17+
extendplan.o \
1718
clauses.o \
1819
inherit.o \
1920
joininfo.o \
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* extendplan.c
4+
* Extend core planner objects with additional private state
5+
*
6+
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7+
* Portions Copyright (c) 1994-5, Regents of the University of California
8+
*
9+
* The interfaces defined in this file make it possible for loadable
10+
* modules to their own private state inside of key planner data
11+
* structures -- specifically, the PlannerGlobal, PlannerInfo, and
12+
* RelOptInfo structures. This can make it much easier to write
13+
* reasonably efficient planner extensions; for instance, code that
14+
* uses set_join_pathlist_hook can arrange to compute a key intermediate
15+
* result once per joinrel rather than on every call.
16+
*
17+
* IDENTIFICATION
18+
* src/backend/commands/extendplan.c
19+
*
20+
*-------------------------------------------------------------------------
21+
*/
22+
#include "postgres.h"
23+
24+
#include "optimizer/extendplan.h"
25+
#include "port/pg_bitutils.h"
26+
#include "utils/memutils.h"
27+
#include "utils/palloc.h"
28+
29+
static const char **PlannerExtensionNameArray = NULL;
30+
static int PlannerExtensionNamesAssigned = 0;
31+
static int PlannerExtensionNamesAllocated = 0;
32+
33+
/*
34+
* Map the name of a planner extension to an integer ID.
35+
*
36+
* Within the lifetime of a particular backend, the same name will be mapped
37+
* to the same ID every time. IDs are not stable across backends. Use the ID
38+
* that you get from this function to call the remaining functions in this
39+
* file.
40+
*/
41+
int
42+
GetPlannerExtensionId(const char *extension_name)
43+
{
44+
/* Search for an existing extension by this name; if found, return ID. */
45+
for (int i = 0; i < PlannerExtensionNamesAssigned; ++i)
46+
if (strcmp(PlannerExtensionNameArray[i], extension_name) == 0)
47+
return i;
48+
49+
/* If there is no array yet, create one. */
50+
if (PlannerExtensionNameArray == NULL)
51+
{
52+
PlannerExtensionNamesAllocated = 16;
53+
PlannerExtensionNameArray = (const char **)
54+
MemoryContextAlloc(TopMemoryContext,
55+
PlannerExtensionNamesAllocated
56+
* sizeof(char *));
57+
}
58+
59+
/* If there's an array but it's currently full, expand it. */
60+
if (PlannerExtensionNamesAssigned >= PlannerExtensionNamesAllocated)
61+
{
62+
int i = pg_nextpower2_32(PlannerExtensionNamesAssigned + 1);
63+
64+
PlannerExtensionNameArray = (const char **)
65+
repalloc(PlannerExtensionNameArray, i * sizeof(char *));
66+
PlannerExtensionNamesAllocated = i;
67+
}
68+
69+
/* Assign and return new ID. */
70+
PlannerExtensionNameArray[PlannerExtensionNamesAssigned] = extension_name;
71+
return PlannerExtensionNamesAssigned++;
72+
}
73+
74+
/*
75+
* Store extension-specific state into a PlannerGlobal.
76+
*/
77+
void
78+
SetPlannerGlobalExtensionState(PlannerGlobal *glob, int extension_id,
79+
void *opaque)
80+
{
81+
Assert(extension_id >= 0);
82+
83+
/* If there is no array yet, create one. */
84+
if (glob->extension_state == NULL)
85+
{
86+
MemoryContext planner_cxt;
87+
Size sz;
88+
89+
planner_cxt = GetMemoryChunkContext(glob);
90+
glob->extension_state_allocated = 4;
91+
sz = glob->extension_state_allocated * sizeof(void *);
92+
glob->extension_state = MemoryContextAllocZero(planner_cxt, sz);
93+
}
94+
95+
/* If there's an array but it's currently full, expand it. */
96+
if (extension_id >= glob->extension_state_allocated)
97+
{
98+
int i;
99+
100+
i = pg_nextpower2_32(glob->extension_state_allocated + 1);
101+
glob->extension_state = (void **)
102+
repalloc0(glob->extension_state,
103+
glob->extension_state_allocated * sizeof(void *),
104+
i * sizeof(void *));
105+
glob->extension_state_allocated = i;
106+
}
107+
108+
glob->extension_state[extension_id] = opaque;
109+
}
110+
111+
/*
112+
* Store extension-specific state into a PlannerInfo.
113+
*/
114+
void
115+
SetPlannerInfoExtensionState(PlannerInfo *root, int extension_id,
116+
void *opaque)
117+
{
118+
Assert(extension_id >= 0);
119+
120+
/* If there is no array yet, create one. */
121+
if (root->extension_state == NULL)
122+
{
123+
Size sz;
124+
125+
root->extension_state_allocated = 4;
126+
sz = root->extension_state_allocated * sizeof(void *);
127+
root->extension_state = MemoryContextAllocZero(root->planner_cxt, sz);
128+
}
129+
130+
/* If there's an array but it's currently full, expand it. */
131+
if (extension_id >= root->extension_state_allocated)
132+
{
133+
int i;
134+
135+
i = pg_nextpower2_32(root->extension_state_allocated + 1);
136+
root->extension_state = (void **)
137+
repalloc0(root->extension_state,
138+
root->extension_state_allocated * sizeof(void *),
139+
i * sizeof(void *));
140+
root->extension_state_allocated = i;
141+
}
142+
143+
root->extension_state[extension_id] = opaque;
144+
}
145+
146+
/*
147+
* Store extension-specific state into a RelOptInfo.
148+
*/
149+
void
150+
SetRelOptInfoExtensionState(RelOptInfo *rel, int extension_id,
151+
void *opaque)
152+
{
153+
Assert(extension_id >= 0);
154+
155+
/* If there is no array yet, create one. */
156+
if (rel->extension_state == NULL)
157+
{
158+
MemoryContext planner_cxt;
159+
Size sz;
160+
161+
planner_cxt = GetMemoryChunkContext(rel);
162+
rel->extension_state_allocated = 4;
163+
sz = rel->extension_state_allocated * sizeof(void *);
164+
rel->extension_state = MemoryContextAllocZero(planner_cxt, sz);
165+
}
166+
167+
/* If there's an array but it's currently full, expand it. */
168+
if (extension_id >= rel->extension_state_allocated)
169+
{
170+
int i;
171+
172+
i = pg_nextpower2_32(rel->extension_state_allocated + 1);
173+
rel->extension_state = (void **)
174+
repalloc0(rel->extension_state,
175+
rel->extension_state_allocated * sizeof(void *),
176+
i * sizeof(void *));
177+
rel->extension_state_allocated = i;
178+
}
179+
180+
rel->extension_state[extension_id] = opaque;
181+
}

src/backend/optimizer/util/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
backend_sources += files(
44
'appendinfo.c',
55
'clauses.c',
6+
'extendplan.c',
67
'inherit.c',
78
'joininfo.c',
89
'orclauses.c',

src/include/nodes/pathnodes.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ typedef struct PlannerGlobal
182182

183183
/* hash table for NOT NULL attnums of relations */
184184
struct HTAB *rel_notnullatts_hash pg_node_attr(read_write_ignore);
185+
186+
/* extension state */
187+
void **extension_state pg_node_attr(read_write_ignore);
188+
int extension_state_allocated;
185189
} PlannerGlobal;
186190

187191
/* macro for fetching the Plan associated with a SubPlan node */
@@ -586,6 +590,10 @@ struct PlannerInfo
586590

587591
/* PartitionPruneInfos added in this query's plan. */
588592
List *partPruneInfos;
593+
594+
/* extension state */
595+
void **extension_state pg_node_attr(read_write_ignore);
596+
int extension_state_allocated;
589597
};
590598

591599

@@ -1097,6 +1105,10 @@ typedef struct RelOptInfo
10971105
List **partexprs pg_node_attr(read_write_ignore);
10981106
/* Nullable partition key expressions */
10991107
List **nullable_partexprs pg_node_attr(read_write_ignore);
1108+
1109+
/* extension state */
1110+
void **extension_state pg_node_attr(read_write_ignore);
1111+
int extension_state_allocated;
11001112
} RelOptInfo;
11011113

11021114
/*

src/include/optimizer/extendplan.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* extendplan.h
4+
* Extend core planner objects with additional private state
5+
*
6+
*
7+
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8+
* Portions Copyright (c) 1994, Regents of the University of California
9+
*
10+
* src/include/optimizer/extendplan.h
11+
*
12+
*-------------------------------------------------------------------------
13+
*/
14+
#ifndef EXTENDPLAN_H
15+
#define EXTENDPLAN_H
16+
17+
#include "nodes/pathnodes.h"
18+
19+
extern int GetPlannerExtensionId(const char *extension_name);
20+
21+
/*
22+
* Get extension-specific state from a PlannerGlobal.
23+
*/
24+
static inline void *
25+
GetPlannerGlobalExtensionState(PlannerGlobal *glob, int extension_id)
26+
{
27+
Assert(extension_id >= 0);
28+
29+
if (extension_id >= glob->extension_state_allocated)
30+
return NULL;
31+
32+
return glob->extension_state[extension_id];
33+
}
34+
35+
/*
36+
* Get extension-specific state from a PlannerInfo.
37+
*/
38+
static inline void *
39+
GetPlannerInfoExtensionState(PlannerInfo *root, int extension_id)
40+
{
41+
Assert(extension_id >= 0);
42+
43+
if (extension_id >= root->extension_state_allocated)
44+
return NULL;
45+
46+
return root->extension_state[extension_id];
47+
}
48+
49+
/*
50+
* Get extension-specific state from a PlannerInfo.
51+
*/
52+
static inline void *
53+
GetRelOptInfoExtensionState(RelOptInfo *rel, int extension_id)
54+
{
55+
Assert(extension_id >= 0);
56+
57+
if (extension_id >= rel->extension_state_allocated)
58+
return NULL;
59+
60+
return rel->extension_state[extension_id];
61+
}
62+
63+
/* Functions to store private state into various planner objects */
64+
extern void SetPlannerGlobalExtensionState(PlannerGlobal *glob,
65+
int extension_id,
66+
void *opaque);
67+
extern void SetPlannerInfoExtensionState(PlannerInfo *root, int extension_id,
68+
void *opaque);
69+
extern void SetRelOptInfoExtensionState(RelOptInfo *root, int extension_id,
70+
void *opaque);
71+
72+
#endif

0 commit comments

Comments
 (0)