/Users/deen/code/yugabyte-db/src/postgres/src/backend/catalog/indexing.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * indexing.c |
4 | | * This file contains routines to support indexes defined on system |
5 | | * catalogs. |
6 | | * |
7 | | * Portions Copyright (c) 1996-2018, 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/htup_details.h" |
19 | | #include "catalog/index.h" |
20 | | #include "catalog/indexing.h" |
21 | | #include "executor/executor.h" |
22 | | #include "utils/syscache.h" |
23 | | #include "utils/rel.h" |
24 | | |
25 | | #include "pg_yb_utils.h" |
26 | | #include "access/yb_scan.h" |
27 | | #include "executor/ybcModifyTable.h" |
28 | | #include "miscadmin.h" |
29 | | |
30 | | /* |
31 | | * CatalogOpenIndexes - open the indexes on a system catalog. |
32 | | * |
33 | | * When inserting or updating tuples in a system catalog, call this |
34 | | * to prepare to update the indexes for the catalog. |
35 | | * |
36 | | * In the current implementation, we share code for opening/closing the |
37 | | * indexes with execUtils.c. But we do not use ExecInsertIndexTuples, |
38 | | * because we don't want to create an EState. This implies that we |
39 | | * do not support partial or expressional indexes on system catalogs, |
40 | | * nor can we support generalized exclusion constraints. |
41 | | * This could be fixed with localized changes here if we wanted to pay |
42 | | * the extra overhead of building an EState. |
43 | | */ |
44 | | CatalogIndexState |
45 | | CatalogOpenIndexes(Relation heapRel) |
46 | 210k | { |
47 | 210k | ResultRelInfo *resultRelInfo; |
48 | | |
49 | 210k | resultRelInfo = makeNode(ResultRelInfo); |
50 | 0 | resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ |
51 | 210k | resultRelInfo->ri_RelationDesc = heapRel; |
52 | 210k | resultRelInfo->ri_TrigDesc = NULL; /* we don't fire triggers */ |
53 | | |
54 | 210k | ExecOpenIndices(resultRelInfo, false); |
55 | | |
56 | 210k | return resultRelInfo; |
57 | 210k | } |
58 | | |
59 | | /* |
60 | | * CatalogCloseIndexes - clean up resources allocated by CatalogOpenIndexes |
61 | | */ |
62 | | void |
63 | | CatalogCloseIndexes(CatalogIndexState indstate) |
64 | 210k | { |
65 | 210k | ExecCloseIndices(indstate); |
66 | 210k | pfree(indstate); |
67 | 210k | } |
68 | | |
69 | | /* |
70 | | * CatalogIndexInsert - insert index entries for one catalog tuple |
71 | | * |
72 | | * This should be called for each inserted or updated catalog tuple. |
73 | | * |
74 | | * This is effectively a cut-down version of ExecInsertIndexTuples. |
75 | | * |
76 | | * if yb_shared_insert is specified, this insert will be done in every |
77 | | * database (including template0 and template1). This is needed when |
78 | | * creating shared relations. |
79 | | * This flag should not be used during initdb bootstrap. |
80 | | */ |
81 | | static void |
82 | | CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple, bool yb_shared_insert) |
83 | 145k | { |
84 | 145k | int i; |
85 | 145k | int numIndexes; |
86 | 145k | RelationPtr relationDescs; |
87 | 145k | Relation heapRelation; |
88 | 145k | TupleTableSlot *slot; |
89 | 145k | IndexInfo **indexInfoArray; |
90 | 145k | Datum values[INDEX_MAX_KEYS]; |
91 | 145k | bool isnull[INDEX_MAX_KEYS]; |
92 | | |
93 | | /* HOT update does not require index inserts */ |
94 | 145k | if (HeapTupleIsHeapOnly(heapTuple)) |
95 | 0 | return; |
96 | | |
97 | | /* |
98 | | * Get information from the state structure. Fall out if nothing to do. |
99 | | */ |
100 | 145k | numIndexes = indstate->ri_NumIndices; |
101 | 145k | if (numIndexes == 0) |
102 | 2.75k | return; |
103 | 142k | relationDescs = indstate->ri_IndexRelationDescs; |
104 | 142k | indexInfoArray = indstate->ri_IndexRelationInfo; |
105 | 142k | heapRelation = indstate->ri_RelationDesc; |
106 | | |
107 | | /* Need a slot to hold the tuple being examined */ |
108 | 142k | slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); |
109 | 142k | ExecStoreHeapTuple(heapTuple, slot, false); |
110 | | |
111 | | /* |
112 | | * for each index, form and insert the index tuple |
113 | | */ |
114 | 444k | for (i = 0; i < numIndexes; i++301k ) |
115 | 301k | { |
116 | | /* |
117 | | * No need to update YugaByte primary key which is intrinic part of |
118 | | * the base table. |
119 | | */ |
120 | 301k | if (IsYugaByteEnabled() && relationDescs[i]->rd_index->indisprimary301k ) |
121 | 92.7k | continue; |
122 | | |
123 | 209k | IndexInfo *indexInfo; |
124 | | |
125 | 209k | indexInfo = indexInfoArray[i]; |
126 | | |
127 | | /* If the index is marked as read-only, ignore it */ |
128 | 209k | 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 | 209k | Assert(indexInfo->ii_Expressions == NIL); |
136 | 209k | Assert(indexInfo->ii_Predicate == NIL); |
137 | 209k | Assert(indexInfo->ii_ExclusionOps == NULL); |
138 | 209k | Assert(relationDescs[i]->rd_index->indimmediate); |
139 | 209k | Assert(indexInfo->ii_NumIndexKeyAttrs != 0); |
140 | | |
141 | | /* |
142 | | * FormIndexDatum fills in its values and isnull parameters with the |
143 | | * appropriate values for the column(s) of the index. |
144 | | */ |
145 | 209k | FormIndexDatum(indexInfo, |
146 | 209k | slot, |
147 | 209k | NULL, /* no expression eval to do */ |
148 | 209k | values, |
149 | 209k | isnull); |
150 | | |
151 | | /* |
152 | | * The index AM does the rest. |
153 | | */ |
154 | 209k | index_insert(relationDescs[i], /* index relation */ |
155 | 209k | values, /* array of index Datums */ |
156 | 209k | isnull, /* is-null flags */ |
157 | 209k | &(heapTuple->t_self), /* tid of heap tuple */ |
158 | 209k | heapTuple, /* heap tuple */ |
159 | 209k | heapRelation, |
160 | 209k | relationDescs[i]->rd_index->indisunique ? |
161 | 124k | UNIQUE_CHECK_YES84.3k : UNIQUE_CHECK_NO, |
162 | 209k | indexInfo, |
163 | 209k | yb_shared_insert); |
164 | 209k | } |
165 | | |
166 | 142k | ExecDropSingleTupleTableSlot(slot); |
167 | 142k | } |
168 | | |
169 | | /* |
170 | | * CatalogIndexDelete - delete index entries for one catalog tuple |
171 | | * |
172 | | * This should be called for each updated or deleted catalog tuple. |
173 | | * |
174 | | * This is effectively a cut-down version of ExecDeleteIndexTuples. |
175 | | */ |
176 | | static void |
177 | | CatalogIndexDelete(CatalogIndexState indstate, HeapTuple heapTuple) |
178 | 113k | { |
179 | 113k | int i; |
180 | 113k | int numIndexes; |
181 | 113k | RelationPtr relationDescs; |
182 | 113k | Relation heapRelation; |
183 | 113k | TupleTableSlot *slot; |
184 | 113k | IndexInfo **indexInfoArray; |
185 | 113k | Datum values[INDEX_MAX_KEYS]; |
186 | 113k | bool isnull[INDEX_MAX_KEYS]; |
187 | | |
188 | | /* |
189 | | * Get information from the state structure. Fall out if nothing to do. |
190 | | */ |
191 | 113k | numIndexes = indstate->ri_NumIndices; |
192 | 113k | if (numIndexes == 0) |
193 | 0 | return; |
194 | 113k | relationDescs = indstate->ri_IndexRelationDescs; |
195 | 113k | indexInfoArray = indstate->ri_IndexRelationInfo; |
196 | 113k | heapRelation = indstate->ri_RelationDesc; |
197 | | |
198 | | /* Need a slot to hold the tuple being examined */ |
199 | 113k | slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); |
200 | 113k | ExecStoreHeapTuple(heapTuple, slot, false); |
201 | | |
202 | | /* |
203 | | * for each index, form and delete the index tuple |
204 | | */ |
205 | 357k | for (i = 0; i < numIndexes; i++243k ) |
206 | 243k | { |
207 | | /* |
208 | | * No need to update YugaByte primary key which is intrinic part of |
209 | | * the base table. |
210 | | */ |
211 | 243k | if (IsYugaByteEnabled() && relationDescs[i]->rd_index->indisprimary) |
212 | 72.6k | continue; |
213 | | |
214 | 170k | IndexInfo *indexInfo; |
215 | | |
216 | 170k | indexInfo = indexInfoArray[i]; |
217 | | |
218 | | /* If the index is marked as read-only, ignore it */ |
219 | 170k | if (!indexInfo->ii_ReadyForInserts) |
220 | 0 | continue; |
221 | | |
222 | | /* |
223 | | * Expressional and partial indexes on system catalogs are not |
224 | | * supported, nor exclusion constraints, nor deferred uniqueness |
225 | | */ |
226 | 170k | Assert(indexInfo->ii_Expressions == NIL); |
227 | 170k | Assert(indexInfo->ii_Predicate == NIL); |
228 | 170k | Assert(indexInfo->ii_ExclusionOps == NULL); |
229 | 170k | Assert(relationDescs[i]->rd_index->indimmediate); |
230 | | |
231 | | /* |
232 | | * FormIndexDatum fills in its values and isnull parameters with the |
233 | | * appropriate values for the column(s) of the index. |
234 | | */ |
235 | 170k | FormIndexDatum(indexInfo, |
236 | 170k | slot, |
237 | 170k | NULL, /* no expression eval to do */ |
238 | 170k | values, |
239 | 170k | isnull); |
240 | | |
241 | | /* |
242 | | * The index AM does the rest. |
243 | | */ |
244 | 170k | index_delete(relationDescs[i], /* index relation */ |
245 | 170k | values, /* array of index Datums */ |
246 | 170k | isnull, /* is-null flags */ |
247 | 170k | heapTuple->t_ybctid, /* heap tuple */ |
248 | 170k | heapRelation, |
249 | 170k | indexInfo); |
250 | 170k | } |
251 | | |
252 | 113k | ExecDropSingleTupleTableSlot(slot); |
253 | 113k | } |
254 | | |
255 | | /* |
256 | | * CatalogTupleInsert - do heap and indexing work for a new catalog tuple |
257 | | * |
258 | | * Insert the tuple data in "tup" into the specified catalog relation. |
259 | | * The Oid of the inserted tuple is returned. |
260 | | * |
261 | | * This is a convenience routine for the common case of inserting a single |
262 | | * tuple in a system catalog; it inserts a new heap tuple, keeping indexes |
263 | | * current. Avoid using it for multiple tuples, since opening the indexes |
264 | | * and building the index info structures is moderately expensive. |
265 | | * (Use CatalogTupleInsertWithInfo in such cases.) |
266 | | */ |
267 | | Oid |
268 | | CatalogTupleInsert(Relation heapRel, HeapTuple tup) |
269 | 26.7k | { |
270 | 26.7k | return YBCatalogTupleInsert(heapRel, tup, false); |
271 | 26.7k | } |
272 | | |
273 | | /* |
274 | | * Enhanced version of CatalogTupleInsert. |
275 | | * |
276 | | * if yb_shared_insert is specified, this insert will be done in every |
277 | | * database (including template0 and template1). This is needed when |
278 | | * creating shared relations. |
279 | | * This flag should not be used during initdb bootstrap. |
280 | | */ |
281 | | Oid |
282 | | YBCatalogTupleInsert(Relation heapRel, HeapTuple tup, bool yb_shared_insert) |
283 | 48.6k | { |
284 | 48.6k | CatalogIndexState indstate; |
285 | 48.6k | Oid oid; |
286 | | |
287 | 48.6k | if (IsYugaByteEnabled()) |
288 | 48.6k | { |
289 | 48.6k | if (yb_shared_insert) |
290 | 41 | { |
291 | 41 | if (!IsYsqlUpgrade) |
292 | 0 | elog(ERROR, "shared insert cannot be done outside of YSQL upgrade"); |
293 | | |
294 | 246 | YB_FOR_EACH_DB41 (pg_db_tuple) |
295 | 246 | { |
296 | 246 | Oid dboid = HeapTupleGetOid(pg_db_tuple); |
297 | | /* |
298 | | * Since this is a catalog table, we assume it exists in all databases. |
299 | | * YB doesn't use PG locks so it's okay not to take them. |
300 | | */ |
301 | 246 | if (dboid == YBCGetDatabaseOid(heapRel)) |
302 | 41 | continue; /* Will be done after the loop. */ |
303 | 205 | YBCExecuteInsertForDb(dboid, heapRel, RelationGetDescr(heapRel), tup); |
304 | 205 | } |
305 | 205 | YB_FOR_EACH_DB_END; |
306 | 82 | } |
307 | 48.6k | oid = YBCExecuteInsertForDb(YBCGetDatabaseOid(heapRel), |
308 | 48.6k | heapRel, |
309 | 48.6k | RelationGetDescr(heapRel), |
310 | 48.6k | tup); |
311 | | /* Update the local cache automatically */ |
312 | 48.6k | YBSetSysCacheTuple(heapRel, tup); |
313 | 48.6k | } |
314 | 0 | else |
315 | 0 | { |
316 | 0 | oid = simple_heap_insert(heapRel, tup); |
317 | 0 | } |
318 | | |
319 | 48.6k | indstate = CatalogOpenIndexes(heapRel); |
320 | | |
321 | 48.6k | CatalogIndexInsert(indstate, tup, yb_shared_insert); |
322 | 48.6k | CatalogCloseIndexes(indstate); |
323 | | |
324 | 48.6k | return oid; |
325 | 48.6k | } |
326 | | |
327 | | /* |
328 | | * CatalogTupleInsertWithInfo - as above, but with caller-supplied index info |
329 | | * |
330 | | * This should be used when it's important to amortize CatalogOpenIndexes/ |
331 | | * CatalogCloseIndexes work across multiple insertions. At some point we |
332 | | * might cache the CatalogIndexState data somewhere (perhaps in the relcache) |
333 | | * so that callers needn't trouble over this ... but we don't do so today. |
334 | | * |
335 | | * if yb_shared_insert is specified, this insert will be done in every |
336 | | * database (including template0 and template1). This is needed when |
337 | | * creating shared relations. |
338 | | * This flag should not be used during initdb bootstrap. |
339 | | */ |
340 | | Oid |
341 | | CatalogTupleInsertWithInfo(Relation heapRel, HeapTuple tup, |
342 | | CatalogIndexState indstate, bool yb_shared_insert) |
343 | 89.6k | { |
344 | 89.6k | Oid oid; |
345 | | |
346 | 89.6k | if (IsYugaByteEnabled()) |
347 | 89.6k | { |
348 | 89.6k | if (yb_shared_insert) |
349 | 106 | { |
350 | 106 | if (!IsYsqlUpgrade) |
351 | 0 | elog(ERROR, "shared insert cannot be done outside of YSQL upgrade"); |
352 | | |
353 | 636 | YB_FOR_EACH_DB106 (pg_db_tuple) |
354 | 636 | { |
355 | 636 | Oid dboid = HeapTupleGetOid(pg_db_tuple); |
356 | | /* |
357 | | * Since this is a catalog table, we assume it exists in all databases. |
358 | | * YB doesn't use PG locks so it's okay not to take them. |
359 | | */ |
360 | 636 | if (dboid == YBCGetDatabaseOid(heapRel)) |
361 | 106 | continue; /* Will be done after the loop. */ |
362 | 530 | YBCExecuteInsertForDb(dboid, heapRel, RelationGetDescr(heapRel), tup); |
363 | 530 | } |
364 | 530 | YB_FOR_EACH_DB_END; |
365 | 212 | } |
366 | 89.6k | oid = YBCExecuteInsertForDb(YBCGetDatabaseOid(heapRel), |
367 | 89.6k | heapRel, |
368 | 89.6k | RelationGetDescr(heapRel), |
369 | 89.6k | tup); |
370 | | /* Update the local cache automatically */ |
371 | 89.6k | YBSetSysCacheTuple(heapRel, tup); |
372 | 89.6k | } |
373 | 0 | else |
374 | 0 | { |
375 | 0 | oid = simple_heap_insert(heapRel, tup); |
376 | 0 | } |
377 | | |
378 | 89.6k | CatalogIndexInsert(indstate, tup, yb_shared_insert); |
379 | | |
380 | 89.6k | return oid; |
381 | 89.6k | } |
382 | | |
383 | | /* |
384 | | * CatalogTupleUpdate - do heap and indexing work for updating a catalog tuple |
385 | | * |
386 | | * Update the tuple identified by "otid", replacing it with the data in "tup". |
387 | | * |
388 | | * This is a convenience routine for the common case of updating a single |
389 | | * tuple in a system catalog; it updates one heap tuple, keeping indexes |
390 | | * current. Avoid using it for multiple tuples, since opening the indexes |
391 | | * and building the index info structures is moderately expensive. |
392 | | * (Use CatalogTupleUpdateWithInfo in such cases.) |
393 | | */ |
394 | | void |
395 | | CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup) |
396 | 7.83k | { |
397 | 7.83k | CatalogIndexState indstate; |
398 | | |
399 | 7.83k | indstate = CatalogOpenIndexes(heapRel); |
400 | | |
401 | 7.83k | if (IsYugaByteEnabled()) |
402 | 7.83k | { |
403 | 7.83k | HeapTuple oldtup = NULL; |
404 | 7.83k | bool has_indices = YBRelHasSecondaryIndices(heapRel); |
405 | | |
406 | 7.83k | if (has_indices) |
407 | 7.43k | { |
408 | 7.43k | if (tup->t_ybctid) |
409 | 7.43k | { |
410 | 7.43k | oldtup = YBCFetchTuple(heapRel, tup->t_ybctid); |
411 | 7.43k | CatalogIndexDelete(indstate, oldtup); |
412 | 7.43k | } |
413 | 0 | else |
414 | 0 | YBC_LOG_WARNING("ybctid missing in %s's tuple", |
415 | 7.43k | RelationGetRelationName(heapRel)); |
416 | 7.43k | } |
417 | | |
418 | 7.83k | YBCUpdateSysCatalogTuple(heapRel, oldtup, tup); |
419 | | /* Update the local cache automatically */ |
420 | 7.83k | YBSetSysCacheTuple(heapRel, tup); |
421 | | |
422 | 7.83k | if (has_indices) |
423 | 7.43k | CatalogIndexInsert(indstate, tup, false /* yb_shared_insert */); |
424 | 7.83k | } |
425 | 0 | else |
426 | 0 | { |
427 | 0 | simple_heap_update(heapRel, otid, tup); |
428 | |
|
429 | 0 | CatalogIndexInsert(indstate, tup, false /* yb_shared_insert */); |
430 | 0 | } |
431 | | |
432 | 7.83k | CatalogCloseIndexes(indstate); |
433 | 7.83k | } |
434 | | |
435 | | /* |
436 | | * CatalogTupleUpdateWithInfo - as above, but with caller-supplied index info |
437 | | * |
438 | | * This should be used when it's important to amortize CatalogOpenIndexes/ |
439 | | * CatalogCloseIndexes work across multiple updates. At some point we |
440 | | * might cache the CatalogIndexState data somewhere (perhaps in the relcache) |
441 | | * so that callers needn't trouble over this ... but we don't do so today. |
442 | | */ |
443 | | void |
444 | | CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, |
445 | | CatalogIndexState indstate) |
446 | 47 | { |
447 | 47 | if (IsYugaByteEnabled()) |
448 | 47 | { |
449 | 47 | HeapTuple oldtup = NULL; |
450 | 47 | bool has_indices = YBRelHasSecondaryIndices(heapRel); |
451 | | |
452 | 47 | if (has_indices) |
453 | 46 | { |
454 | 46 | if (tup->t_ybctid) |
455 | 46 | { |
456 | 46 | oldtup = YBCFetchTuple(heapRel, tup->t_ybctid); |
457 | 46 | CatalogIndexDelete(indstate, oldtup); |
458 | 46 | } |
459 | 0 | else |
460 | 0 | YBC_LOG_WARNING("ybctid missing in %s's tuple", |
461 | 46 | RelationGetRelationName(heapRel)); |
462 | 46 | } |
463 | | |
464 | 47 | YBCUpdateSysCatalogTuple(heapRel, oldtup, tup); |
465 | | /* Update the local cache automatically */ |
466 | 47 | YBSetSysCacheTuple(heapRel, tup); |
467 | | |
468 | 47 | if (has_indices) |
469 | 46 | CatalogIndexInsert(indstate, tup, false /* yb_shared_insert */); |
470 | 47 | } |
471 | 0 | else |
472 | 0 | { |
473 | 0 | simple_heap_update(heapRel, otid, tup); |
474 | |
|
475 | 0 | CatalogIndexInsert(indstate, tup, false /* yb_shared_insert */); |
476 | 0 | } |
477 | 47 | } |
478 | | |
479 | | /* |
480 | | * CatalogTupleDelete - do heap and indexing work for deleting a catalog tuple |
481 | | * |
482 | | * Delete the tuple identified by "tid" in the specified catalog. |
483 | | * |
484 | | * With Postgres heaps, there is no index work to do at deletion time; |
485 | | * cleanup will be done later by VACUUM. However, callers of this function |
486 | | * shouldn't have to know that; we'd like a uniform abstraction for all |
487 | | * catalog tuple changes. Hence, provide this currently-trivial wrapper. |
488 | | * |
489 | | * The abstraction is a bit leaky in that we don't provide an optimized |
490 | | * CatalogTupleDeleteWithInfo version, because there is currently nothing to |
491 | | * optimize. If we ever need that, rather than touching a lot of call sites, |
492 | | * it might be better to do something about caching CatalogIndexState. |
493 | | */ |
494 | | void |
495 | | CatalogTupleDelete(Relation heapRel, HeapTuple tup) |
496 | 106k | { |
497 | 106k | if (IsYugaByteEnabled()) |
498 | 106k | { |
499 | 106k | YBCDeleteSysCatalogTuple(heapRel, tup); |
500 | | |
501 | 106k | CatalogIndexState indstate = CatalogOpenIndexes(heapRel); |
502 | | |
503 | 106k | CatalogIndexDelete(indstate, tup); |
504 | 106k | CatalogCloseIndexes(indstate); |
505 | 106k | } |
506 | 0 | else |
507 | 0 | { |
508 | 0 | simple_heap_delete(heapRel, &tup->t_self); |
509 | 0 | } |
510 | 106k | } |