/src/postgres/src/backend/catalog/indexing.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * indexing.c |
4 | | * This file contains routines to support indexes defined on system |
5 | | * catalogs. |
6 | | * |
7 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
8 | | * Portions Copyright (c) 1994, Regents of the University of California |
9 | | * |
10 | | * |
11 | | * IDENTIFICATION |
12 | | * src/backend/catalog/indexing.c |
13 | | * |
14 | | *------------------------------------------------------------------------- |
15 | | */ |
16 | | #include "postgres.h" |
17 | | |
18 | | #include "access/genam.h" |
19 | | #include "access/heapam.h" |
20 | | #include "access/htup_details.h" |
21 | | #include "access/xact.h" |
22 | | #include "catalog/index.h" |
23 | | #include "catalog/indexing.h" |
24 | | #include "executor/executor.h" |
25 | | #include "utils/rel.h" |
26 | | |
27 | | |
28 | | /* |
29 | | * CatalogOpenIndexes - open the indexes on a system catalog. |
30 | | * |
31 | | * When inserting or updating tuples in a system catalog, call this |
32 | | * to prepare to update the indexes for the catalog. |
33 | | * |
34 | | * In the current implementation, we share code for opening/closing the |
35 | | * indexes with execUtils.c. But we do not use ExecInsertIndexTuples, |
36 | | * because we don't want to create an EState. This implies that we |
37 | | * do not support partial or expressional indexes on system catalogs, |
38 | | * nor can we support generalized exclusion constraints. |
39 | | * This could be fixed with localized changes here if we wanted to pay |
40 | | * the extra overhead of building an EState. |
41 | | */ |
42 | | CatalogIndexState |
43 | | CatalogOpenIndexes(Relation heapRel) |
44 | 0 | { |
45 | 0 | ResultRelInfo *resultRelInfo; |
46 | |
|
47 | 0 | resultRelInfo = makeNode(ResultRelInfo); |
48 | 0 | resultRelInfo->ri_RangeTableIndex = 0; /* dummy */ |
49 | 0 | resultRelInfo->ri_RelationDesc = heapRel; |
50 | 0 | resultRelInfo->ri_TrigDesc = NULL; /* we don't fire triggers */ |
51 | |
|
52 | 0 | ExecOpenIndices(resultRelInfo, false); |
53 | |
|
54 | 0 | return resultRelInfo; |
55 | 0 | } |
56 | | |
57 | | /* |
58 | | * CatalogCloseIndexes - clean up resources allocated by CatalogOpenIndexes |
59 | | */ |
60 | | void |
61 | | CatalogCloseIndexes(CatalogIndexState indstate) |
62 | 0 | { |
63 | 0 | ExecCloseIndices(indstate); |
64 | 0 | pfree(indstate); |
65 | 0 | } |
66 | | |
67 | | /* |
68 | | * CatalogIndexInsert - insert index entries for one catalog tuple |
69 | | * |
70 | | * This should be called for each inserted or updated catalog tuple. |
71 | | * |
72 | | * This is effectively a cut-down version of ExecInsertIndexTuples. |
73 | | */ |
74 | | static void |
75 | | CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple, |
76 | | TU_UpdateIndexes updateIndexes) |
77 | 0 | { |
78 | 0 | int i; |
79 | 0 | int numIndexes; |
80 | 0 | RelationPtr relationDescs; |
81 | 0 | Relation heapRelation; |
82 | 0 | TupleTableSlot *slot; |
83 | 0 | IndexInfo **indexInfoArray; |
84 | 0 | Datum values[INDEX_MAX_KEYS]; |
85 | 0 | bool isnull[INDEX_MAX_KEYS]; |
86 | 0 | bool onlySummarized = (updateIndexes == TU_Summarizing); |
87 | | |
88 | | /* |
89 | | * HOT update does not require index inserts. But with asserts enabled we |
90 | | * want to check that it'd be legal to currently insert into the |
91 | | * table/index. |
92 | | */ |
93 | 0 | #ifndef USE_ASSERT_CHECKING |
94 | 0 | if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized) |
95 | 0 | return; |
96 | 0 | #endif |
97 | | |
98 | | /* When only updating summarized indexes, the tuple has to be HOT. */ |
99 | 0 | Assert((!onlySummarized) || HeapTupleIsHeapOnly(heapTuple)); |
100 | | |
101 | | /* |
102 | | * Get information from the state structure. Fall out if nothing to do. |
103 | | */ |
104 | 0 | numIndexes = indstate->ri_NumIndices; |
105 | 0 | if (numIndexes == 0) |
106 | 0 | return; |
107 | 0 | relationDescs = indstate->ri_IndexRelationDescs; |
108 | 0 | indexInfoArray = indstate->ri_IndexRelationInfo; |
109 | 0 | heapRelation = indstate->ri_RelationDesc; |
110 | | |
111 | | /* Need a slot to hold the tuple being examined */ |
112 | 0 | slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation), |
113 | 0 | &TTSOpsHeapTuple); |
114 | 0 | ExecStoreHeapTuple(heapTuple, slot, false); |
115 | | |
116 | | /* |
117 | | * for each index, form and insert the index tuple |
118 | | */ |
119 | 0 | for (i = 0; i < numIndexes; i++) |
120 | 0 | { |
121 | 0 | IndexInfo *indexInfo; |
122 | 0 | Relation index; |
123 | |
|
124 | 0 | indexInfo = indexInfoArray[i]; |
125 | 0 | index = relationDescs[i]; |
126 | | |
127 | | /* If the index is marked as read-only, ignore it */ |
128 | 0 | if (!indexInfo->ii_ReadyForInserts) |
129 | 0 | continue; |
130 | | |
131 | | /* |
132 | | * Expressional and partial indexes on system catalogs are not |
133 | | * supported, nor exclusion constraints, nor deferred uniqueness |
134 | | */ |
135 | 0 | Assert(indexInfo->ii_Expressions == NIL); |
136 | 0 | Assert(indexInfo->ii_Predicate == NIL); |
137 | 0 | Assert(indexInfo->ii_ExclusionOps == NULL); |
138 | 0 | Assert(index->rd_index->indimmediate); |
139 | 0 | Assert(indexInfo->ii_NumIndexKeyAttrs != 0); |
140 | | |
141 | | /* see earlier check above */ |
142 | | #ifdef USE_ASSERT_CHECKING |
143 | | if (HeapTupleIsHeapOnly(heapTuple) && !onlySummarized) |
144 | | { |
145 | | Assert(!ReindexIsProcessingIndex(RelationGetRelid(index))); |
146 | | continue; |
147 | | } |
148 | | #endif /* USE_ASSERT_CHECKING */ |
149 | | |
150 | | /* |
151 | | * Skip insertions into non-summarizing indexes if we only need to |
152 | | * update summarizing indexes. |
153 | | */ |
154 | 0 | if (onlySummarized && !indexInfo->ii_Summarizing) |
155 | 0 | continue; |
156 | | |
157 | | /* |
158 | | * FormIndexDatum fills in its values and isnull parameters with the |
159 | | * appropriate values for the column(s) of the index. |
160 | | */ |
161 | 0 | FormIndexDatum(indexInfo, |
162 | 0 | slot, |
163 | 0 | NULL, /* no expression eval to do */ |
164 | 0 | values, |
165 | 0 | isnull); |
166 | | |
167 | | /* |
168 | | * The index AM does the rest. |
169 | | */ |
170 | 0 | index_insert(index, /* index relation */ |
171 | 0 | values, /* array of index Datums */ |
172 | 0 | isnull, /* is-null flags */ |
173 | 0 | &(heapTuple->t_self), /* tid of heap tuple */ |
174 | 0 | heapRelation, |
175 | 0 | index->rd_index->indisunique ? |
176 | 0 | UNIQUE_CHECK_YES : UNIQUE_CHECK_NO, |
177 | 0 | false, |
178 | 0 | indexInfo); |
179 | 0 | } |
180 | |
|
181 | 0 | ExecDropSingleTupleTableSlot(slot); |
182 | 0 | } |
183 | | |
184 | | /* |
185 | | * Subroutine to verify that catalog constraints are honored. |
186 | | * |
187 | | * Tuples inserted via CatalogTupleInsert/CatalogTupleUpdate are generally |
188 | | * "hand made", so that it's possible that they fail to satisfy constraints |
189 | | * that would be checked if they were being inserted by the executor. That's |
190 | | * a coding error, so we only bother to check for it in assert-enabled builds. |
191 | | */ |
192 | | #ifdef USE_ASSERT_CHECKING |
193 | | |
194 | | static void |
195 | | CatalogTupleCheckConstraints(Relation heapRel, HeapTuple tup) |
196 | | { |
197 | | /* |
198 | | * Currently, the only constraints implemented for system catalogs are |
199 | | * attnotnull constraints. |
200 | | */ |
201 | | if (HeapTupleHasNulls(tup)) |
202 | | { |
203 | | TupleDesc tupdesc = RelationGetDescr(heapRel); |
204 | | bits8 *bp = tup->t_data->t_bits; |
205 | | |
206 | | for (int attnum = 0; attnum < tupdesc->natts; attnum++) |
207 | | { |
208 | | Form_pg_attribute thisatt = TupleDescAttr(tupdesc, attnum); |
209 | | |
210 | | Assert(!(thisatt->attnotnull && att_isnull(attnum, bp))); |
211 | | } |
212 | | } |
213 | | } |
214 | | |
215 | | #else /* !USE_ASSERT_CHECKING */ |
216 | | |
217 | 0 | #define CatalogTupleCheckConstraints(heapRel, tup) ((void) 0) |
218 | | |
219 | | #endif /* USE_ASSERT_CHECKING */ |
220 | | |
221 | | /* |
222 | | * CatalogTupleInsert - do heap and indexing work for a new catalog tuple |
223 | | * |
224 | | * Insert the tuple data in "tup" into the specified catalog relation. |
225 | | * |
226 | | * This is a convenience routine for the common case of inserting a single |
227 | | * tuple in a system catalog; it inserts a new heap tuple, keeping indexes |
228 | | * current. Avoid using it for multiple tuples, since opening the indexes |
229 | | * and building the index info structures is moderately expensive. |
230 | | * (Use CatalogTupleInsertWithInfo in such cases.) |
231 | | */ |
232 | | void |
233 | | CatalogTupleInsert(Relation heapRel, HeapTuple tup) |
234 | 0 | { |
235 | 0 | CatalogIndexState indstate; |
236 | |
|
237 | 0 | CatalogTupleCheckConstraints(heapRel, tup); |
238 | |
|
239 | 0 | indstate = CatalogOpenIndexes(heapRel); |
240 | |
|
241 | 0 | simple_heap_insert(heapRel, tup); |
242 | |
|
243 | 0 | CatalogIndexInsert(indstate, tup, TU_All); |
244 | 0 | CatalogCloseIndexes(indstate); |
245 | 0 | } |
246 | | |
247 | | /* |
248 | | * CatalogTupleInsertWithInfo - as above, but with caller-supplied index info |
249 | | * |
250 | | * This should be used when it's important to amortize CatalogOpenIndexes/ |
251 | | * CatalogCloseIndexes work across multiple insertions. At some point we |
252 | | * might cache the CatalogIndexState data somewhere (perhaps in the relcache) |
253 | | * so that callers needn't trouble over this ... but we don't do so today. |
254 | | */ |
255 | | void |
256 | | CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, |
257 | | CatalogIndexState indstate) |
258 | 0 | { |
259 | 0 | CatalogTupleCheckConstraints(heapRel, tup); |
260 | |
|
261 | 0 | simple_heap_insert(heapRel, tup); |
262 | |
|
263 | 0 | CatalogIndexInsert(indstate, tup, TU_All); |
264 | 0 | } |
265 | | |
266 | | /* |
267 | | * CatalogTuplesMultiInsertWithInfo - as above, but for multiple tuples |
268 | | * |
269 | | * Insert multiple tuples into the given catalog relation at once, with an |
270 | | * amortized cost of CatalogOpenIndexes. |
271 | | */ |
272 | | void |
273 | | CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, |
274 | | int ntuples, CatalogIndexState indstate) |
275 | 0 | { |
276 | | /* Nothing to do */ |
277 | 0 | if (ntuples <= 0) |
278 | 0 | return; |
279 | | |
280 | 0 | heap_multi_insert(heapRel, slot, ntuples, |
281 | 0 | GetCurrentCommandId(true), 0, NULL); |
282 | | |
283 | | /* |
284 | | * There is no equivalent to heap_multi_insert for the catalog indexes, so |
285 | | * we must loop over and insert individually. |
286 | | */ |
287 | 0 | for (int i = 0; i < ntuples; i++) |
288 | 0 | { |
289 | 0 | bool should_free; |
290 | 0 | HeapTuple tuple; |
291 | |
|
292 | 0 | tuple = ExecFetchSlotHeapTuple(slot[i], true, &should_free); |
293 | 0 | tuple->t_tableOid = slot[i]->tts_tableOid; |
294 | 0 | CatalogIndexInsert(indstate, tuple, TU_All); |
295 | |
|
296 | 0 | if (should_free) |
297 | 0 | heap_freetuple(tuple); |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | /* |
302 | | * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple |
303 | | * |
304 | | * Update the tuple identified by "otid", replacing it with the data in "tup". |
305 | | * |
306 | | * This is a convenience routine for the common case of updating a single |
307 | | * tuple in a system catalog; it updates one heap tuple, keeping indexes |
308 | | * current. Avoid using it for multiple tuples, since opening the indexes |
309 | | * and building the index info structures is moderately expensive. |
310 | | * (Use CatalogTupleUpdateWithInfo in such cases.) |
311 | | */ |
312 | | void |
313 | | CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup) |
314 | 0 | { |
315 | 0 | CatalogIndexState indstate; |
316 | 0 | TU_UpdateIndexes updateIndexes = TU_All; |
317 | |
|
318 | 0 | CatalogTupleCheckConstraints(heapRel, tup); |
319 | |
|
320 | 0 | indstate = CatalogOpenIndexes(heapRel); |
321 | |
|
322 | 0 | simple_heap_update(heapRel, otid, tup, &updateIndexes); |
323 | |
|
324 | 0 | CatalogIndexInsert(indstate, tup, updateIndexes); |
325 | 0 | CatalogCloseIndexes(indstate); |
326 | 0 | } |
327 | | |
328 | | /* |
329 | | * CatalogTupleUpdateWithInfo - as above, but with caller-supplied index info |
330 | | * |
331 | | * This should be used when it's important to amortize CatalogOpenIndexes/ |
332 | | * CatalogCloseIndexes work across multiple updates. At some point we |
333 | | * might cache the CatalogIndexState data somewhere (perhaps in the relcache) |
334 | | * so that callers needn't trouble over this ... but we don't do so today. |
335 | | */ |
336 | | void |
337 | | CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, |
338 | | CatalogIndexState indstate) |
339 | 0 | { |
340 | 0 | TU_UpdateIndexes updateIndexes = TU_All; |
341 | |
|
342 | 0 | CatalogTupleCheckConstraints(heapRel, tup); |
343 | |
|
344 | 0 | simple_heap_update(heapRel, otid, tup, &updateIndexes); |
345 | |
|
346 | 0 | CatalogIndexInsert(indstate, tup, updateIndexes); |
347 | 0 | } |
348 | | |
349 | | /* |
350 | | * CatalogTupleDelete - do heap and indexing work for deleting a catalog tuple |
351 | | * |
352 | | * Delete the tuple identified by "tid" in the specified catalog. |
353 | | * |
354 | | * With Postgres heaps, there is no index work to do at deletion time; |
355 | | * cleanup will be done later by VACUUM. However, callers of this function |
356 | | * shouldn't have to know that; we'd like a uniform abstraction for all |
357 | | * catalog tuple changes. Hence, provide this currently-trivial wrapper. |
358 | | * |
359 | | * The abstraction is a bit leaky in that we don't provide an optimized |
360 | | * CatalogTupleDeleteWithInfo version, because there is currently nothing to |
361 | | * optimize. If we ever need that, rather than touching a lot of call sites, |
362 | | * it might be better to do something about caching CatalogIndexState. |
363 | | */ |
364 | | void |
365 | | CatalogTupleDelete(Relation heapRel, ItemPointer tid) |
366 | 0 | { |
367 | 0 | simple_heap_delete(heapRel, tid); |
368 | 0 | } |