Coverage Report

Created: 2025-09-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}