/src/postgres/src/backend/optimizer/util/extendplan.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * extendplan.c |
4 | | * Extend core planner objects with additional private state |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, 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 store 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/optimizer/util/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 | | |
28 | | static const char **PlannerExtensionNameArray = NULL; |
29 | | static int PlannerExtensionNamesAssigned = 0; |
30 | | static int PlannerExtensionNamesAllocated = 0; |
31 | | |
32 | | /* |
33 | | * Map the name of a planner extension to an integer ID. |
34 | | * |
35 | | * Within the lifetime of a particular backend, the same name will be mapped |
36 | | * to the same ID every time. IDs are not stable across backends. Use the ID |
37 | | * that you get from this function to call the remaining functions in this |
38 | | * file. |
39 | | */ |
40 | | int |
41 | | GetPlannerExtensionId(const char *extension_name) |
42 | 0 | { |
43 | | /* Search for an existing extension by this name; if found, return ID. */ |
44 | 0 | for (int i = 0; i < PlannerExtensionNamesAssigned; ++i) |
45 | 0 | if (strcmp(PlannerExtensionNameArray[i], extension_name) == 0) |
46 | 0 | return i; |
47 | | |
48 | | /* If there is no array yet, create one. */ |
49 | 0 | if (PlannerExtensionNameArray == NULL) |
50 | 0 | { |
51 | 0 | PlannerExtensionNamesAllocated = 16; |
52 | 0 | PlannerExtensionNameArray = (const char **) |
53 | 0 | MemoryContextAlloc(TopMemoryContext, |
54 | 0 | PlannerExtensionNamesAllocated |
55 | 0 | * sizeof(char *)); |
56 | 0 | } |
57 | | |
58 | | /* If there's an array but it's currently full, expand it. */ |
59 | 0 | if (PlannerExtensionNamesAssigned >= PlannerExtensionNamesAllocated) |
60 | 0 | { |
61 | 0 | int i = pg_nextpower2_32(PlannerExtensionNamesAssigned + 1); |
62 | |
|
63 | 0 | PlannerExtensionNameArray = repalloc_array(PlannerExtensionNameArray, const char *, i); |
64 | 0 | PlannerExtensionNamesAllocated = i; |
65 | 0 | } |
66 | | |
67 | | /* Assign and return new ID. */ |
68 | 0 | PlannerExtensionNameArray[PlannerExtensionNamesAssigned] = extension_name; |
69 | 0 | return PlannerExtensionNamesAssigned++; |
70 | 0 | } |
71 | | |
72 | | /* |
73 | | * Store extension-specific state into a PlannerGlobal. |
74 | | */ |
75 | | void |
76 | | SetPlannerGlobalExtensionState(PlannerGlobal *glob, int extension_id, |
77 | | void *opaque) |
78 | 0 | { |
79 | 0 | Assert(extension_id >= 0); |
80 | | |
81 | | /* If there is no array yet, create one. */ |
82 | 0 | if (glob->extension_state == NULL) |
83 | 0 | { |
84 | 0 | MemoryContext planner_cxt; |
85 | 0 | Size sz; |
86 | |
|
87 | 0 | planner_cxt = GetMemoryChunkContext(glob); |
88 | 0 | glob->extension_state_allocated = |
89 | 0 | Max(4, pg_nextpower2_32(extension_id + 1)); |
90 | 0 | sz = glob->extension_state_allocated * sizeof(void *); |
91 | 0 | glob->extension_state = MemoryContextAllocZero(planner_cxt, sz); |
92 | 0 | } |
93 | | |
94 | | /* If there's an array but it's currently full, expand it. */ |
95 | 0 | if (extension_id >= glob->extension_state_allocated) |
96 | 0 | { |
97 | 0 | int i; |
98 | |
|
99 | 0 | i = pg_nextpower2_32(extension_id + 1); |
100 | 0 | glob->extension_state = repalloc0_array(glob->extension_state, void *, |
101 | 0 | glob->extension_state_allocated, i); |
102 | 0 | glob->extension_state_allocated = i; |
103 | 0 | } |
104 | |
|
105 | 0 | glob->extension_state[extension_id] = opaque; |
106 | 0 | } |
107 | | |
108 | | /* |
109 | | * Store extension-specific state into a PlannerInfo. |
110 | | */ |
111 | | void |
112 | | SetPlannerInfoExtensionState(PlannerInfo *root, int extension_id, |
113 | | void *opaque) |
114 | 0 | { |
115 | 0 | Assert(extension_id >= 0); |
116 | | |
117 | | /* If there is no array yet, create one. */ |
118 | 0 | if (root->extension_state == NULL) |
119 | 0 | { |
120 | 0 | Size sz; |
121 | |
|
122 | 0 | root->extension_state_allocated = |
123 | 0 | Max(4, pg_nextpower2_32(extension_id + 1)); |
124 | 0 | sz = root->extension_state_allocated * sizeof(void *); |
125 | 0 | root->extension_state = MemoryContextAllocZero(root->planner_cxt, sz); |
126 | 0 | } |
127 | | |
128 | | /* If there's an array but it's currently full, expand it. */ |
129 | 0 | if (extension_id >= root->extension_state_allocated) |
130 | 0 | { |
131 | 0 | int i; |
132 | |
|
133 | 0 | i = pg_nextpower2_32(extension_id + 1); |
134 | 0 | root->extension_state = repalloc0_array(root->extension_state, void *, |
135 | 0 | root->extension_state_allocated, i); |
136 | 0 | root->extension_state_allocated = i; |
137 | 0 | } |
138 | |
|
139 | 0 | root->extension_state[extension_id] = opaque; |
140 | 0 | } |
141 | | |
142 | | /* |
143 | | * Store extension-specific state into a RelOptInfo. |
144 | | */ |
145 | | void |
146 | | SetRelOptInfoExtensionState(RelOptInfo *rel, int extension_id, |
147 | | void *opaque) |
148 | 0 | { |
149 | 0 | Assert(extension_id >= 0); |
150 | | |
151 | | /* If there is no array yet, create one. */ |
152 | 0 | if (rel->extension_state == NULL) |
153 | 0 | { |
154 | 0 | MemoryContext planner_cxt; |
155 | 0 | Size sz; |
156 | |
|
157 | 0 | planner_cxt = GetMemoryChunkContext(rel); |
158 | 0 | rel->extension_state_allocated = |
159 | 0 | Max(4, pg_nextpower2_32(extension_id + 1)); |
160 | 0 | sz = rel->extension_state_allocated * sizeof(void *); |
161 | 0 | rel->extension_state = MemoryContextAllocZero(planner_cxt, sz); |
162 | 0 | } |
163 | | |
164 | | /* If there's an array but it's currently full, expand it. */ |
165 | 0 | if (extension_id >= rel->extension_state_allocated) |
166 | 0 | { |
167 | 0 | int i; |
168 | |
|
169 | 0 | i = pg_nextpower2_32(extension_id + 1); |
170 | 0 | rel->extension_state = repalloc0_array(rel->extension_state, void *, |
171 | 0 | rel->extension_state_allocated, i); |
172 | 0 | rel->extension_state_allocated = i; |
173 | 0 | } |
174 | |
|
175 | 0 | rel->extension_state[extension_id] = opaque; |
176 | 0 | } |