Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * genam.c
4 : : * general index access method routines
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/index/genam.c
12 : : *
13 : : * NOTES
14 : : * many of the old access method routines have been turned into
15 : : * macros and moved to genam.h -cim 4/30/91
16 : : *
17 : : *-------------------------------------------------------------------------
18 : : */
19 : :
20 : : #include "postgres.h"
21 : :
22 : : #include "access/genam.h"
23 : : #include "access/heapam.h"
24 : : #include "access/relscan.h"
25 : : #include "access/tableam.h"
26 : : #include "access/transam.h"
27 : : #include "catalog/index.h"
28 : : #include "lib/stringinfo.h"
29 : : #include "miscadmin.h"
30 : : #include "storage/bufmgr.h"
31 : : #include "storage/procarray.h"
32 : : #include "utils/acl.h"
33 : : #include "utils/injection_point.h"
34 : : #include "utils/lsyscache.h"
35 : : #include "utils/rel.h"
36 : : #include "utils/rls.h"
37 : : #include "utils/ruleutils.h"
38 : : #include "utils/snapmgr.h"
39 : :
40 : :
41 : : /* ----------------------------------------------------------------
42 : : * general access method routines
43 : : *
44 : : * All indexed access methods use an identical scan structure.
45 : : * We don't know how the various AMs do locking, however, so we don't
46 : : * do anything about that here.
47 : : *
48 : : * The intent is that an AM implementor will define a beginscan routine
49 : : * that calls RelationGetIndexScan, to fill in the scan, and then does
50 : : * whatever kind of locking he wants.
51 : : *
52 : : * At the end of a scan, the AM's endscan routine undoes the locking,
53 : : * but does *not* call IndexScanEnd --- the higher-level index_endscan
54 : : * routine does that. (We can't do it in the AM because index_endscan
55 : : * still needs to touch the IndexScanDesc after calling the AM.)
56 : : *
57 : : * Because of this, the AM does not have a choice whether to call
58 : : * RelationGetIndexScan or not; its beginscan routine must return an
59 : : * object made by RelationGetIndexScan. This is kinda ugly but not
60 : : * worth cleaning up now.
61 : : * ----------------------------------------------------------------
62 : : */
63 : :
64 : : /* ----------------
65 : : * RelationGetIndexScan -- Create and fill an IndexScanDesc.
66 : : *
67 : : * This routine creates an index scan structure and sets up initial
68 : : * contents for it.
69 : : *
70 : : * Parameters:
71 : : * indexRelation -- index relation for scan.
72 : : * nkeys -- count of scan keys (index qual conditions).
73 : : * norderbys -- count of index order-by operators.
74 : : *
75 : : * Returns:
76 : : * An initialized IndexScanDesc.
77 : : * ----------------
78 : : */
79 : : IndexScanDesc
5771 tgl@sss.pgh.pa.us 80 :CBC 8677977 : RelationGetIndexScan(Relation indexRelation, int nkeys, int norderbys)
81 : : {
82 : : IndexScanDesc scan;
83 : :
284 michael@paquier.xyz 84 : 8677977 : scan = palloc_object(IndexScanDescData);
85 : :
8889 tgl@sss.pgh.pa.us 86 : 8677977 : scan->heapRelation = NULL; /* may be set later */
5 pg@bowt.ie 87 :GNC 8677977 : scan->xs_table_opaque = NULL;
8889 tgl@sss.pgh.pa.us 88 :CBC 8677977 : scan->indexRelation = indexRelation;
3378 89 : 8677977 : scan->xs_snapshot = InvalidSnapshot; /* caller must initialize this */
8889 90 : 8677977 : scan->numberOfKeys = nkeys;
5771 91 : 8677977 : scan->numberOfOrderBys = norderbys;
92 : :
93 : : /*
94 : : * We allocate key workspace here, but it won't get filled until amrescan.
95 : : */
8889 96 [ + + ]: 8677977 : if (nkeys > 0)
284 michael@paquier.xyz 97 : 8669794 : scan->keyData = palloc_array(ScanKeyData, nkeys);
98 : : else
8889 tgl@sss.pgh.pa.us 99 : 8183 : scan->keyData = NULL;
5771 100 [ + + ]: 8677977 : if (norderbys > 0)
284 michael@paquier.xyz 101 : 119 : scan->orderByData = palloc_array(ScanKeyData, norderbys);
102 : : else
5771 tgl@sss.pgh.pa.us 103 : 8677858 : scan->orderByData = NULL;
104 : :
5215 bruce@momjian.us 105 : 8677977 : scan->xs_want_itup = false; /* may be set later */
106 : :
107 : : /*
108 : : * During recovery we ignore killed tuples and don't bother to kill them
109 : : * either. We do this because the xmin on the primary node could easily be
110 : : * later than the xmin on the standby node, so that what the primary
111 : : * thinks is killed is supposed to be visible on standby. So for correct
112 : : * MVCC for queries during recovery we must ignore these hints and check
113 : : * all tuples. Do *not* set ignore_killed_tuples to true when running in a
114 : : * transaction that was started during recovery. xactStartedInRecovery
115 : : * should not be altered by index AMs.
116 : : */
8885 tgl@sss.pgh.pa.us 117 : 8677977 : scan->kill_prior_tuple = false;
6119 simon@2ndQuadrant.co 118 : 8677977 : scan->xactStartedInRecovery = TransactionStartedDuringRecovery();
119 : 8677977 : scan->ignore_killed_tuples = !scan->xactStartedInRecovery;
120 : :
10605 bruce@momjian.us 121 : 8677977 : scan->opaque = NULL;
558 pg@bowt.ie 122 : 8677977 : scan->instrument = NULL;
123 : :
5462 tgl@sss.pgh.pa.us 124 : 8677977 : scan->xs_itup = NULL;
5453 125 : 8677977 : scan->xs_itupdesc = NULL;
3492 126 : 8677977 : scan->xs_hitup = NULL;
127 : 8677977 : scan->xs_hitupdesc = NULL;
128 : :
5 pg@bowt.ie 129 :GNC 8677977 : scan->xs_getnext_slot = NULL;
130 : :
131 : 8677977 : scan->xs_name_cstring_attnums = NULL;
132 : 8677977 : scan->xs_name_cstring_buf = NULL;
133 : 8677977 : scan->xs_name_cstring_count = 0;
134 : :
135 : 8677977 : scan->xs_visited_pages_limit = 0;
136 : :
10246 bruce@momjian.us 137 :CBC 8677977 : return scan;
138 : : }
139 : :
140 : : /* ----------------
141 : : * IndexScanEnd -- End an index scan.
142 : : *
143 : : * This routine just releases the storage acquired by
144 : : * RelationGetIndexScan(). Any AM-level resources are
145 : : * assumed to already have been released by the AM's
146 : : * endscan routine.
147 : : *
148 : : * Returns:
149 : : * None.
150 : : * ----------------
151 : : */
152 : : void
9761 tgl@sss.pgh.pa.us 153 : 8676690 : IndexScanEnd(IndexScanDesc scan)
154 : : {
155 [ + + ]: 8676690 : if (scan->keyData != NULL)
156 : 8668531 : pfree(scan->keyData);
5771 157 [ + + ]: 8676690 : if (scan->orderByData != NULL)
158 : 115 : pfree(scan->orderByData);
5 pg@bowt.ie 159 [ + + ]:GNC 8676690 : if (scan->xs_name_cstring_attnums != NULL)
160 : 5082 : pfree(scan->xs_name_cstring_attnums);
161 [ + + ]: 8676690 : if (scan->xs_name_cstring_buf != NULL)
162 : 5082 : pfree(scan->xs_name_cstring_buf);
163 : :
9761 tgl@sss.pgh.pa.us 164 :CBC 8676690 : pfree(scan);
165 : 8676690 : }
166 : :
167 : : /*
168 : : * BuildIndexValueDescription
169 : : *
170 : : * Construct a string describing the contents of an index entry, in the
171 : : * form "(key_name, ...)=(key_value, ...)". This is currently used
172 : : * for building unique-constraint, exclusion-constraint error messages, and
173 : : * logical replication conflict error messages so only key columns of the index
174 : : * are checked and printed.
175 : : *
176 : : * Note that if the user does not have permissions to view all of the
177 : : * columns involved then a NULL is returned. Returning a partial key seems
178 : : * unlikely to be useful and we have no way to know which of the columns the
179 : : * user provided (unlike in ExecBuildSlotValueDescription).
180 : : *
181 : : * The passed-in values/nulls arrays are the "raw" input to the index AM,
182 : : * e.g. results of FormIndexDatum --- this is not necessarily what is stored
183 : : * in the index, but it's what the user perceives to be stored.
184 : : *
185 : : * Note: if you change anything here, check whether
186 : : * ExecBuildSlotPartitionKeyDescription() in execMain.c needs a similar
187 : : * change.
188 : : */
189 : : char *
6259 190 : 780 : BuildIndexValueDescription(Relation indexRelation,
191 : : const Datum *values, const bool *isnull)
192 : : {
193 : : StringInfoData buf;
194 : : Form_pg_index idxrec;
195 : : int indnkeyatts;
196 : : int i;
197 : : int keyno;
4269 sfrost@snowman.net 198 : 780 : Oid indexrelid = RelationGetRelid(indexRelation);
199 : : Oid indrelid;
200 : : AclResult aclresult;
201 : :
3088 teodor@sigaev.ru 202 : 780 : indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRelation);
203 : :
204 : : /*
205 : : * Check permissions- if the user does not have access to view all of the
206 : : * key columns then return NULL to avoid leaking data.
207 : : *
208 : : * First check if RLS is enabled for the relation. If so, return NULL to
209 : : * avoid leaking data.
210 : : *
211 : : * Next we need to check table-level SELECT access and then, if there is
212 : : * no access there, check column-level permissions.
213 : : */
2988 alvherre@alvh.no-ip. 214 : 780 : idxrec = indexRelation->rd_index;
4269 sfrost@snowman.net 215 : 780 : indrelid = idxrec->indrelid;
216 [ - + ]: 780 : Assert(indexrelid == idxrec->indexrelid);
217 : :
218 : : /* RLS check- if RLS is enabled then we don't return anything. */
4072 mail@joeconway.com 219 [ + + ]: 780 : if (check_enable_rls(indrelid, InvalidOid, true) == RLS_ENABLED)
4269 sfrost@snowman.net 220 : 8 : return NULL;
221 : :
222 : : /* Table-level SELECT is enough, if the user has it */
223 : 772 : aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT);
224 [ + + ]: 772 : if (aclresult != ACLCHECK_OK)
225 : : {
226 : : /*
227 : : * No table-level access, so step through the columns in the index and
228 : : * make sure the user has SELECT rights on all of them.
229 : : */
2988 alvherre@alvh.no-ip. 230 [ + - ]: 16 : for (keyno = 0; keyno < indnkeyatts; keyno++)
231 : : {
4269 sfrost@snowman.net 232 : 16 : AttrNumber attnum = idxrec->indkey.values[keyno];
233 : :
234 : : /*
235 : : * Note that if attnum == InvalidAttrNumber, then this is an index
236 : : * based on an expression and we return no detail rather than try
237 : : * to figure out what column(s) the expression includes and if the
238 : : * user has SELECT rights on them.
239 : : */
4252 240 [ + - + + ]: 32 : if (attnum == InvalidAttrNumber ||
241 : 16 : pg_attribute_aclcheck(indrelid, attnum, GetUserId(),
242 : : ACL_SELECT) != ACLCHECK_OK)
243 : : {
244 : : /* No access, so clean up and return */
4269 245 : 8 : return NULL;
246 : : }
247 : : }
248 : : }
249 : :
6259 tgl@sss.pgh.pa.us 250 : 764 : initStringInfo(&buf);
251 : 764 : appendStringInfo(&buf, "(%s)=(",
252 : : pg_get_indexdef_columns(indexrelid, true));
253 : :
3088 teodor@sigaev.ru 254 [ + + ]: 1779 : for (i = 0; i < indnkeyatts; i++)
255 : : {
256 : : char *val;
257 : :
6259 tgl@sss.pgh.pa.us 258 [ + + ]: 1015 : if (isnull[i])
259 : 28 : val = "null";
260 : : else
261 : : {
262 : : Oid foutoid;
263 : : bool typisvarlena;
264 : :
265 : : /*
266 : : * The provided data is not necessarily of the type stored in the
267 : : * index; rather it is of the index opclass's input type. So look
268 : : * at rd_opcintype not the index tupdesc.
269 : : *
270 : : * Note: this is a bit shaky for opclasses that have pseudotype
271 : : * input types such as ANYARRAY or RECORD. Currently, the
272 : : * typoutput functions associated with the pseudotypes will work
273 : : * okay, but we might have to try harder in future.
274 : : */
6131 275 : 987 : getTypeOutputInfo(indexRelation->rd_opcintype[i],
276 : : &foutoid, &typisvarlena);
6259 277 : 987 : val = OidOutputFunctionCall(foutoid, values[i]);
278 : : }
279 : :
280 [ + + ]: 1015 : if (i > 0)
281 : 251 : appendStringInfoString(&buf, ", ");
282 : 1015 : appendStringInfoString(&buf, val);
283 : : }
284 : :
285 : 764 : appendStringInfoChar(&buf, ')');
286 : :
287 : 764 : return buf.data;
288 : : }
289 : :
290 : : /*
291 : : * Get the snapshotConflictHorizon from the table entries pointed to by the
292 : : * index tuples being deleted using an AM-generic approach.
293 : : *
294 : : * This is a table_index_delete_tuples() shim used by index AMs that only need
295 : : * to consult the tableam to get a snapshotConflictHorizon value, and only
296 : : * expect to delete index tuples that are already known deletable (typically
297 : : * due to having LP_DEAD bits set). When a snapshotConflictHorizon value
298 : : * isn't needed in index AM's deletion WAL record, it is safe for it to skip
299 : : * calling here entirely.
300 : : *
301 : : * We assume that caller index AM uses the standard IndexTuple representation,
302 : : * with table TIDs stored in the t_tid field. We also expect (and assert)
303 : : * that the line pointers on page for 'itemnos' offsets are already marked
304 : : * LP_DEAD.
305 : : */
306 : : TransactionId
2735 andres@anarazel.de 307 : 16 : index_compute_xid_horizon_for_tuples(Relation irel,
308 : : Relation hrel,
309 : : Buffer ibuf,
310 : : OffsetNumber *itemnos,
311 : : int nitems)
312 : : {
313 : : TM_IndexDeleteOp delstate;
1403 pg@bowt.ie 314 : 16 : TransactionId snapshotConflictHorizon = InvalidTransactionId;
2735 andres@anarazel.de 315 : 16 : Page ipage = BufferGetPage(ibuf);
316 : : IndexTuple itup;
317 : :
2063 pg@bowt.ie 318 [ - + ]: 16 : Assert(nitems > 0);
319 : :
1781 320 : 16 : delstate.irel = irel;
321 : 16 : delstate.iblknum = BufferGetBlockNumber(ibuf);
2076 322 : 16 : delstate.bottomup = false;
323 : 16 : delstate.bottomupfreespace = 0;
324 : 16 : delstate.ndeltids = 0;
284 michael@paquier.xyz 325 : 16 : delstate.deltids = palloc_array(TM_IndexDelete, nitems);
326 : 16 : delstate.status = palloc_array(TM_IndexStatus, nitems);
327 : :
328 : : /* identify what the index tuples about to be deleted point to */
2735 andres@anarazel.de 329 [ + + ]: 2530 : for (int i = 0; i < nitems; i++)
330 : : {
1781 pg@bowt.ie 331 : 2514 : OffsetNumber offnum = itemnos[i];
332 : : ItemId iitemid;
333 : :
334 : 2514 : iitemid = PageGetItemId(ipage, offnum);
2735 andres@anarazel.de 335 : 2514 : itup = (IndexTuple) PageGetItem(ipage, iitemid);
336 : :
2076 pg@bowt.ie 337 [ - + ]: 2514 : Assert(ItemIdIsDead(iitemid));
338 : :
339 : 2514 : ItemPointerCopy(&itup->t_tid, &delstate.deltids[i].tid);
340 : 2514 : delstate.deltids[i].id = delstate.ndeltids;
1781 341 : 2514 : delstate.status[i].idxoffnum = offnum;
2076 342 : 2514 : delstate.status[i].knowndeletable = true; /* LP_DEAD-marked */
343 : 2514 : delstate.status[i].promising = false; /* unused */
344 : 2514 : delstate.status[i].freespace = 0; /* unused */
345 : :
346 : 2514 : delstate.ndeltids++;
347 : : }
348 : :
349 : : /* determine the actual xid horizon */
1403 350 : 16 : snapshotConflictHorizon = table_index_delete_tuples(hrel, &delstate);
351 : :
352 : : /* assert tableam agrees that all items are deletable */
2076 353 [ - + ]: 16 : Assert(delstate.ndeltids == nitems);
354 : :
355 : 16 : pfree(delstate.deltids);
356 : 16 : pfree(delstate.status);
357 : :
1403 358 : 16 : return snapshotConflictHorizon;
359 : : }
360 : :
361 : :
362 : : /* ----------------------------------------------------------------
363 : : * heap-or-index-scan access to system catalogs
364 : : *
365 : : * These functions support system catalog accesses that normally use
366 : : * an index but need to be capable of being switched to heap scans
367 : : * if the system indexes are unavailable.
368 : : *
369 : : * The specified scan keys must be compatible with the named index.
370 : : * Generally this means that they must constrain either all columns
371 : : * of the index, or the first K columns of an N-column index.
372 : : *
373 : : * These routines could work with non-system tables, actually,
374 : : * but they're only useful when there is a known index to use with
375 : : * the given scan keys; so in practice they're only good for
376 : : * predetermined types of scans of system catalogs.
377 : : * ----------------------------------------------------------------
378 : : */
379 : :
380 : : /*
381 : : * systable_beginscan --- set up for heap-or-index scan
382 : : *
383 : : * rel: catalog to scan, already opened and suitably locked
384 : : * indexId: OID of index to conditionally use
385 : : * indexOK: if false, forces a heap scan (see notes below)
386 : : * snapshot: time qual to use (NULL for a recent catalog snapshot)
387 : : * nkeys, key: scan keys
388 : : *
389 : : * The attribute numbers in the scan key should be set for the heap case.
390 : : * If we choose to index, we convert them to 1..n to reference the index
391 : : * columns. Note this means there must be one scankey qualification per
392 : : * index column! This is checked by the Asserts in the normal, index-using
393 : : * case, but won't be checked if the heapscan path is taken.
394 : : *
395 : : * The routine checks the normal cases for whether an indexscan is safe,
396 : : * but caller can make additional checks and pass indexOK=false if needed.
397 : : * In standard case indexOK can simply be constant TRUE.
398 : : */
399 : : SysScanDesc
8889 tgl@sss.pgh.pa.us 400 : 8588288 : systable_beginscan(Relation heapRelation,
401 : : Oid indexId,
402 : : bool indexOK,
403 : : Snapshot snapshot,
404 : : int nkeys, ScanKey key)
405 : : {
406 : : SysScanDesc sysscan;
407 : : Relation irel;
408 : :
7829 409 [ + + ]: 8588288 : if (indexOK &&
7563 peter_e@gmx.net 410 [ + + ]: 8443529 : !IgnoreSystemIndexes &&
7829 tgl@sss.pgh.pa.us 411 [ + + ]: 8372111 : !ReindexIsProcessingIndex(indexId))
7356 412 : 8364709 : irel = index_open(indexId, AccessShareLock);
413 : : else
8397 414 : 223579 : irel = NULL;
415 : :
284 michael@paquier.xyz 416 : 8588285 : sysscan = palloc_object(SysScanDescData);
417 : :
8889 tgl@sss.pgh.pa.us 418 : 8588285 : sysscan->heap_rel = heapRelation;
8397 419 : 8588285 : sysscan->irel = irel;
2750 andres@anarazel.de 420 : 8588285 : sysscan->slot = table_slot_create(heapRelation, NULL);
421 : :
4828 rhaas@postgresql.org 422 [ + + ]: 8588285 : if (snapshot == NULL)
423 : : {
4520 bruce@momjian.us 424 : 7901727 : Oid relid = RelationGetRelid(heapRelation);
425 : :
4828 rhaas@postgresql.org 426 : 7901727 : snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
427 : 7901727 : sysscan->snapshot = snapshot;
428 : : }
429 : : else
430 : : {
431 : : /* Caller is responsible for any snapshot. */
432 : 686558 : sysscan->snapshot = NULL;
433 : : }
434 : :
435 : : /*
436 : : * If CheckXidAlive is set then set a flag to indicate that system table
437 : : * scan is in-progress. See detailed comments in xact.c where these
438 : : * variables are declared.
439 : : */
234 andres@anarazel.de 440 [ + + ]: 8588285 : if (TransactionIdIsValid(CheckXidAlive))
441 : 982 : bsysscan = true;
442 : :
8397 tgl@sss.pgh.pa.us 443 [ + + ]: 8588285 : if (irel)
444 : : {
445 : : int i;
446 : : ScanKey idxkey;
447 : :
773 peter@eisentraut.org 448 : 8364706 : idxkey = palloc_array(ScanKeyData, nkeys);
449 : :
450 : : /* Convert attribute numbers to be index column numbers. */
8979 tgl@sss.pgh.pa.us 451 [ + + ]: 22123296 : for (i = 0; i < nkeys; i++)
452 : : {
453 : : int j;
454 : :
773 peter@eisentraut.org 455 : 13758590 : memcpy(&idxkey[i], &key[i], sizeof(ScanKeyData));
456 : :
3088 teodor@sigaev.ru 457 [ + - ]: 20178572 : for (j = 0; j < IndexRelationGetNumberOfAttributes(irel); j++)
458 : : {
6527 heikki.linnakangas@i 459 [ + + ]: 20178572 : if (key[i].sk_attno == irel->rd_index->indkey.values[j])
460 : : {
773 peter@eisentraut.org 461 : 13758590 : idxkey[i].sk_attno = j + 1;
6527 heikki.linnakangas@i 462 : 13758590 : break;
463 : : }
464 : : }
3088 teodor@sigaev.ru 465 [ - + ]: 13758590 : if (j == IndexRelationGetNumberOfAttributes(irel))
6527 heikki.linnakangas@i 466 [ # # ]:UBC 0 : elog(ERROR, "column is not in index");
467 : : }
468 : :
5 pg@bowt.ie 469 :GNC 8364706 : sysscan->iscan = index_beginscan(heapRelation, irel, false,
470 : : snapshot, NULL, nkeys, 0,
471 : : SO_NONE);
773 peter@eisentraut.org 472 :CBC 8364706 : index_rescan(sysscan->iscan, idxkey, nkeys, NULL, 0);
8979 tgl@sss.pgh.pa.us 473 : 8364706 : sysscan->scan = NULL;
474 : :
656 peter@eisentraut.org 475 : 8364706 : pfree(idxkey);
476 : : }
477 : : else
478 : : {
479 : : /*
480 : : * We disallow synchronized scans when forced to use a heapscan on a
481 : : * catalog. In most cases the desired rows are near the front, so
482 : : * that the unpredictable start point of a syncscan is a serious
483 : : * disadvantage; and there are no compensating advantages, because
484 : : * it's unlikely that such scans will occur in parallel.
485 : : */
2750 andres@anarazel.de 486 : 223579 : sysscan->scan = table_beginscan_strat(heapRelation, snapshot,
487 : : nkeys, key,
488 : : true, false);
8979 tgl@sss.pgh.pa.us 489 : 223579 : sysscan->iscan = NULL;
490 : : }
491 : :
492 : 8588285 : return sysscan;
493 : : }
494 : :
495 : : /*
496 : : * HandleConcurrentAbort - Handle concurrent abort of the CheckXidAlive.
497 : : *
498 : : * Error out, if CheckXidAlive is aborted. We can't directly use
499 : : * TransactionIdDidAbort as after crash such transaction might not have been
500 : : * marked as aborted. See detailed comments in xact.c where the variable
501 : : * is declared.
502 : : */
503 : : static inline void
291 nathan@postgresql.or 504 : 17947403 : HandleConcurrentAbort(void)
505 : : {
2234 akapila@postgresql.o 506 [ + + ]: 17947403 : if (TransactionIdIsValid(CheckXidAlive) &&
507 [ + + ]: 1627 : !TransactionIdIsInProgress(CheckXidAlive) &&
508 [ + + ]: 60 : !TransactionIdDidCommit(CheckXidAlive))
509 [ + - ]: 8 : ereport(ERROR,
510 : : (errcode(ERRCODE_TRANSACTION_ROLLBACK),
511 : : errmsg("transaction aborted during system catalog scan")));
512 : 17947395 : }
513 : :
514 : : /*
515 : : * systable_getnext --- get next tuple in a heap-or-index scan
516 : : *
517 : : * Returns NULL if no more tuples available.
518 : : *
519 : : * Note that returned tuple is a reference to data in a disk buffer;
520 : : * it must not be modified, and should be presumed inaccessible after
521 : : * next getnext() or endscan() call.
522 : : *
523 : : * XXX: It'd probably make sense to offer a slot based interface, at least
524 : : * optionally.
525 : : */
526 : : HeapTuple
8979 tgl@sss.pgh.pa.us 527 : 17648277 : systable_getnext(SysScanDesc sysscan)
528 : : {
2750 andres@anarazel.de 529 : 17648277 : HeapTuple htup = NULL;
530 : :
8979 tgl@sss.pgh.pa.us 531 [ + + ]: 17648277 : if (sysscan->irel)
532 : : {
5 pg@bowt.ie 533 [ + + ]:GNC 16014624 : if (table_index_getnext_slot(sysscan->iscan, ForwardScanDirection,
534 : 16014624 : sysscan->slot))
535 : : {
536 : : bool shouldFree;
537 : :
2750 andres@anarazel.de 538 :CBC 12163345 : htup = ExecFetchSlotHeapTuple(sysscan->slot, false, &shouldFree);
539 [ - + ]: 12163345 : Assert(!shouldFree);
540 : :
541 : : /*
542 : : * We currently don't need to support lossy index operators for
543 : : * any system catalog scan. It could be done here, using the scan
544 : : * keys to drive the operator calls, if we arranged to save the
545 : : * heap attnums during systable_beginscan(); this is practical
546 : : * because we still wouldn't need to support indexes on
547 : : * expressions.
548 : : */
549 [ - + ]: 12163345 : if (sysscan->iscan->xs_recheck)
2750 andres@anarazel.de 550 [ # # ]:UBC 0 : elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
551 : : }
552 : : }
553 : : else
554 : : {
2750 andres@anarazel.de 555 [ + + ]:CBC 1633653 : if (table_scan_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot))
556 : : {
557 : : bool shouldFree;
558 : :
559 : 1581883 : htup = ExecFetchSlotHeapTuple(sysscan->slot, false, &shouldFree);
560 [ - + ]: 1581883 : Assert(!shouldFree);
561 : : }
562 : : }
563 : :
564 : : /*
565 : : * Handle the concurrent abort while fetching the catalog tuple during
566 : : * logical streaming of a transaction.
567 : : */
2234 akapila@postgresql.o 568 : 17648273 : HandleConcurrentAbort();
569 : :
8979 tgl@sss.pgh.pa.us 570 : 17648265 : return htup;
571 : : }
572 : :
573 : : /*
574 : : * systable_recheck_tuple --- recheck visibility of most-recently-fetched tuple
575 : : *
576 : : * In particular, determine if this tuple would be visible to a catalog scan
577 : : * that started now. We don't handle the case of a non-MVCC scan snapshot,
578 : : * because no caller needs that yet.
579 : : *
580 : : * This is useful to test whether an object was deleted while we waited to
581 : : * acquire lock on it.
582 : : *
583 : : * Note: we don't actually *need* the tuple to be passed in, but it's a
584 : : * good crosscheck that the caller is interested in the right tuple.
585 : : */
586 : : bool
6678 587 : 154501 : systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
588 : : {
589 : : Snapshot freshsnap;
590 : : bool result;
591 : :
2750 andres@anarazel.de 592 [ - + ]: 154501 : Assert(tup == ExecFetchSlotHeapTuple(sysscan->slot, false, NULL));
593 : :
4814 noah@leadboat.com 594 : 154501 : freshsnap = GetCatalogSnapshot(RelationGetRelid(sysscan->heap_rel));
558 heikki.linnakangas@i 595 : 154501 : freshsnap = RegisterSnapshot(freshsnap);
596 : :
2750 andres@anarazel.de 597 : 154501 : result = table_tuple_satisfies_snapshot(sysscan->heap_rel,
598 : 154501 : sysscan->slot,
599 : : freshsnap);
558 heikki.linnakangas@i 600 : 154501 : UnregisterSnapshot(freshsnap);
601 : :
602 : : /*
603 : : * Handle the concurrent abort while fetching the catalog tuple during
604 : : * logical streaming of a transaction.
605 : : */
2234 akapila@postgresql.o 606 : 154501 : HandleConcurrentAbort();
607 : :
6678 tgl@sss.pgh.pa.us 608 : 154501 : return result;
609 : : }
610 : :
611 : : /*
612 : : * systable_endscan --- close scan, release resources
613 : : *
614 : : * Note that it's still up to the caller to close the heap relation.
615 : : */
616 : : void
8979 617 : 8587719 : systable_endscan(SysScanDesc sysscan)
618 : : {
2750 andres@anarazel.de 619 [ + - ]: 8587719 : if (sysscan->slot)
620 : : {
621 : 8587719 : ExecDropSingleTupleTableSlot(sysscan->slot);
622 : 8587719 : sysscan->slot = NULL;
623 : : }
624 : :
8979 tgl@sss.pgh.pa.us 625 [ + + ]: 8587719 : if (sysscan->irel)
626 : : {
627 : 8364151 : index_endscan(sysscan->iscan);
7356 628 : 8364151 : index_close(sysscan->irel, AccessShareLock);
629 : : }
630 : : else
2750 andres@anarazel.de 631 : 223568 : table_endscan(sysscan->scan);
632 : :
4828 rhaas@postgresql.org 633 [ + + ]: 8587719 : if (sysscan->snapshot)
634 : 7901169 : UnregisterSnapshot(sysscan->snapshot);
635 : :
636 : : /*
637 : : * Reset the bsysscan flag at the end of the systable scan. See detailed
638 : : * comments in xact.c where these variables are declared.
639 : : */
2234 akapila@postgresql.o 640 [ + + ]: 8587719 : if (TransactionIdIsValid(CheckXidAlive))
641 : 974 : bsysscan = false;
642 : :
8979 tgl@sss.pgh.pa.us 643 : 8587719 : pfree(sysscan);
644 : 8587719 : }
645 : :
646 : :
647 : : /*
648 : : * systable_beginscan_ordered --- set up for ordered catalog scan
649 : : *
650 : : * These routines have essentially the same API as systable_beginscan etc,
651 : : * except that they guarantee to return multiple matching tuples in
652 : : * index order. Also, for largely historical reasons, the index to use
653 : : * is opened and locked by the caller, not here.
654 : : *
655 : : * Currently we do not support non-index-based scans here. (In principle
656 : : * we could do a heapscan and sort, but the uses are in places that
657 : : * probably don't need to still work with corrupted catalog indexes.)
658 : : * For the moment, therefore, these functions are merely the thinnest of
659 : : * wrappers around index_beginscan/table_index_getnext_slot. The main reason
660 : : * for their existence is to centralize possible future support of lossy
661 : : * operators in catalog scans.
662 : : */
663 : : SysScanDesc
6735 664 : 37328 : systable_beginscan_ordered(Relation heapRelation,
665 : : Relation indexRelation,
666 : : Snapshot snapshot,
667 : : int nkeys, ScanKey key)
668 : : {
669 : : SysScanDesc sysscan;
670 : : int i;
671 : : ScanKey idxkey;
672 : :
673 : : /* REINDEX can probably be a hard error here ... */
674 [ - + ]: 37328 : if (ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))
938 tgl@sss.pgh.pa.us 675 [ # # ]:UBC 0 : ereport(ERROR,
676 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
677 : : errmsg("cannot access index \"%s\" while it is being reindexed",
678 : : RelationGetRelationName(indexRelation))));
679 : : /* ... but we only throw a warning about violating IgnoreSystemIndexes */
6735 tgl@sss.pgh.pa.us 680 [ - + ]:CBC 37328 : if (IgnoreSystemIndexes)
6735 tgl@sss.pgh.pa.us 681 [ # # ]:UBC 0 : elog(WARNING, "using index \"%s\" despite IgnoreSystemIndexes",
682 : : RelationGetRelationName(indexRelation));
683 : :
284 michael@paquier.xyz 684 :CBC 37328 : sysscan = palloc_object(SysScanDescData);
685 : :
6735 tgl@sss.pgh.pa.us 686 : 37328 : sysscan->heap_rel = heapRelation;
687 : 37328 : sysscan->irel = indexRelation;
2750 andres@anarazel.de 688 : 37328 : sysscan->slot = table_slot_create(heapRelation, NULL);
689 : :
4828 rhaas@postgresql.org 690 [ + + ]: 37328 : if (snapshot == NULL)
691 : : {
4520 bruce@momjian.us 692 : 5316 : Oid relid = RelationGetRelid(heapRelation);
693 : :
4828 rhaas@postgresql.org 694 : 5316 : snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
695 : 5316 : sysscan->snapshot = snapshot;
696 : : }
697 : : else
698 : : {
699 : : /* Caller is responsible for any snapshot. */
700 : 32012 : sysscan->snapshot = NULL;
701 : : }
702 : :
773 peter@eisentraut.org 703 : 37328 : idxkey = palloc_array(ScanKeyData, nkeys);
704 : :
705 : : /* Convert attribute numbers to be index column numbers. */
6735 tgl@sss.pgh.pa.us 706 [ + + ]: 72729 : for (i = 0; i < nkeys; i++)
707 : : {
708 : : int j;
709 : :
773 peter@eisentraut.org 710 : 35401 : memcpy(&idxkey[i], &key[i], sizeof(ScanKeyData));
711 : :
3088 teodor@sigaev.ru 712 [ + - ]: 37520 : for (j = 0; j < IndexRelationGetNumberOfAttributes(indexRelation); j++)
713 : : {
6527 heikki.linnakangas@i 714 [ + + ]: 37520 : if (key[i].sk_attno == indexRelation->rd_index->indkey.values[j])
715 : : {
773 peter@eisentraut.org 716 : 35401 : idxkey[i].sk_attno = j + 1;
6527 heikki.linnakangas@i 717 : 35401 : break;
718 : : }
719 : : }
3088 teodor@sigaev.ru 720 [ - + ]: 35401 : if (j == IndexRelationGetNumberOfAttributes(indexRelation))
6527 heikki.linnakangas@i 721 [ # # ]:UBC 0 : elog(ERROR, "column is not in index");
722 : : }
723 : :
724 : : /*
725 : : * If CheckXidAlive is set then set a flag to indicate that system table
726 : : * scan is in-progress. See detailed comments in xact.c where these
727 : : * variables are declared.
728 : : */
713 akapila@postgresql.o 729 [ - + ]:CBC 37328 : if (TransactionIdIsValid(CheckXidAlive))
713 akapila@postgresql.o 730 :UBC 0 : bsysscan = true;
731 : :
5 pg@bowt.ie 732 :GNC 37328 : sysscan->iscan = index_beginscan(heapRelation, indexRelation, false,
733 : : snapshot, NULL, nkeys, 0,
734 : : SO_NONE);
234 andres@anarazel.de 735 :CBC 37328 : index_rescan(sysscan->iscan, idxkey, nkeys, NULL, 0);
736 : 37328 : sysscan->scan = NULL;
737 : :
738 : 37328 : pfree(idxkey);
739 : :
6735 tgl@sss.pgh.pa.us 740 : 37328 : return sysscan;
741 : : }
742 : :
743 : : /*
744 : : * systable_getnext_ordered --- get next tuple in an ordered catalog scan
745 : : */
746 : : HeapTuple
747 : 144632 : systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
748 : : {
2750 andres@anarazel.de 749 : 144632 : HeapTuple htup = NULL;
750 : :
6735 tgl@sss.pgh.pa.us 751 [ - + ]: 144632 : Assert(sysscan->irel);
5 pg@bowt.ie 752 [ + + ]:GNC 144632 : if (table_index_getnext_slot(sysscan->iscan, direction, sysscan->slot))
2750 andres@anarazel.de 753 :CBC 108156 : htup = ExecFetchSlotHeapTuple(sysscan->slot, false, NULL);
754 : :
755 : : /* See notes in systable_getnext */
6734 tgl@sss.pgh.pa.us 756 [ + + - + ]: 144629 : if (htup && sysscan->iscan->xs_recheck)
6734 tgl@sss.pgh.pa.us 757 [ # # ]:UBC 0 : elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
758 : :
759 : : /*
760 : : * Handle the concurrent abort while fetching the catalog tuple during
761 : : * logical streaming of a transaction.
762 : : */
2234 akapila@postgresql.o 763 :CBC 144629 : HandleConcurrentAbort();
764 : :
6735 tgl@sss.pgh.pa.us 765 : 144629 : return htup;
766 : : }
767 : :
768 : : /*
769 : : * systable_endscan_ordered --- close scan, release resources
770 : : */
771 : : void
772 : 37317 : systable_endscan_ordered(SysScanDesc sysscan)
773 : : {
2750 andres@anarazel.de 774 [ + - ]: 37317 : if (sysscan->slot)
775 : : {
776 : 37317 : ExecDropSingleTupleTableSlot(sysscan->slot);
777 : 37317 : sysscan->slot = NULL;
778 : : }
779 : :
6735 tgl@sss.pgh.pa.us 780 [ - + ]: 37317 : Assert(sysscan->irel);
781 : 37317 : index_endscan(sysscan->iscan);
4828 rhaas@postgresql.org 782 [ + + ]: 37317 : if (sysscan->snapshot)
783 : 5308 : UnregisterSnapshot(sysscan->snapshot);
784 : :
785 : : /*
786 : : * Reset the bsysscan flag at the end of the systable scan. See detailed
787 : : * comments in xact.c where these variables are declared.
788 : : */
713 akapila@postgresql.o 789 [ - + ]: 37317 : if (TransactionIdIsValid(CheckXidAlive))
713 akapila@postgresql.o 790 :UBC 0 : bsysscan = false;
791 : :
6735 tgl@sss.pgh.pa.us 792 :CBC 37317 : pfree(sysscan);
793 : 37317 : }
794 : :
795 : : /*
796 : : * systable_inplace_update_begin --- update a row "in place" (overwrite it)
797 : : *
798 : : * Overwriting violates both MVCC and transactional safety, so the uses of
799 : : * this function in Postgres are extremely limited. This makes no effort to
800 : : * support updating cache key columns or other indexed columns. Nonetheless
801 : : * we find some places to use it. See README.tuplock section "Locking to
802 : : * write inplace-updated tables" and later sections for expectations of
803 : : * readers and writers of a table that gets inplace updates. Standard flow:
804 : : *
805 : : * ... [any slow preparation not requiring oldtup] ...
806 : : * systable_inplace_update_begin([...], &tup, &inplace_state);
807 : : * if (!HeapTupleIsValid(tup))
808 : : * elog(ERROR, [...]);
809 : : * ... [buffer is exclusive-locked; mutate "tup"] ...
810 : : * if (dirty)
811 : : * systable_inplace_update_finish(inplace_state, tup);
812 : : * else
813 : : * systable_inplace_update_cancel(inplace_state);
814 : : *
815 : : * The first several params duplicate the systable_beginscan() param list.
816 : : * "oldtupcopy" is an output parameter, assigned NULL if the key ceases to
817 : : * find a live tuple. (In PROC_IN_VACUUM, that is a low-probability transient
818 : : * condition.) If "oldtupcopy" gets non-NULL, you must pass output parameter
819 : : * "state" to systable_inplace_update_finish() or
820 : : * systable_inplace_update_cancel().
821 : : */
822 : : void
726 noah@leadboat.com 823 : 114316 : systable_inplace_update_begin(Relation relation,
824 : : Oid indexId,
825 : : bool indexOK,
826 : : Snapshot snapshot,
827 : : int nkeys, const ScanKeyData *key,
828 : : HeapTuple *oldtupcopy,
829 : : void **state)
830 : : {
831 : 114316 : int retries = 0;
832 : : SysScanDesc scan;
833 : : HeapTuple oldtup;
834 : : BufferHeapTupleTableSlot *bslot;
835 : :
836 : : /*
837 : : * For now, we don't allow parallel updates. Unlike a regular update,
838 : : * this should never create a combo CID, so it might be possible to relax
839 : : * this restriction, but not without more thought and testing. It's not
840 : : * clear that it would be useful, anyway.
841 : : */
842 [ - + ]: 114316 : if (IsInParallelMode())
726 noah@leadboat.com 843 [ # # ]:UBC 0 : ereport(ERROR,
844 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
845 : : errmsg("cannot update tuples during a parallel operation")));
846 : :
847 : : /*
848 : : * Accept a snapshot argument, for symmetry, but this function advances
849 : : * its snapshot as needed to reach the tail of the updated tuple chain.
850 : : */
726 noah@leadboat.com 851 [ - + ]:CBC 114316 : Assert(snapshot == NULL);
852 : :
853 [ - + - - ]: 114316 : Assert(IsInplaceUpdateRelation(relation) || !IsSystemRelation(relation));
854 : :
855 : : /* Loop for an exclusive-locked buffer of a non-updated tuple. */
856 : : do
857 : : {
858 : : TupleTableSlot *slot;
859 : :
860 [ + + ]: 114372 : CHECK_FOR_INTERRUPTS();
861 : :
862 : : /*
863 : : * Processes issuing heap_update (e.g. GRANT) at maximum speed could
864 : : * drive us to this error. A hostile table owner has stronger ways to
865 : : * damage their own table, so that's minor.
866 : : */
867 [ - + ]: 114372 : if (retries++ > 10000)
726 noah@leadboat.com 868 [ # # ]:UBC 0 : elog(ERROR, "giving up after too many tries to overwrite row");
869 : :
498 michael@paquier.xyz 870 :CBC 114372 : INJECTION_POINT("inplace-before-pin", NULL);
726 noah@leadboat.com 871 : 114372 : scan = systable_beginscan(relation, indexId, indexOK, snapshot,
872 : : nkeys, unconstify(ScanKeyData *, key));
873 : 114372 : oldtup = systable_getnext(scan);
874 [ - + ]: 114372 : if (!HeapTupleIsValid(oldtup))
875 : : {
726 noah@leadboat.com 876 :UBC 0 : systable_endscan(scan);
877 : 0 : *oldtupcopy = NULL;
878 : 0 : return;
879 : : }
880 : :
726 noah@leadboat.com 881 :CBC 114372 : slot = scan->slot;
882 [ - + ]: 114372 : Assert(TTS_IS_BUFFERTUPLE(slot));
883 : 114372 : bslot = (BufferHeapTupleTableSlot *) slot;
691 884 [ + + ]: 114372 : } while (!heap_inplace_lock(scan->heap_rel,
885 : : bslot->base.tuple, bslot->buffer,
886 : : (void (*) (void *)) systable_endscan, scan));
887 : :
726 888 : 114316 : *oldtupcopy = heap_copytuple(oldtup);
889 : 114316 : *state = scan;
890 : : }
891 : :
892 : : /*
893 : : * systable_inplace_update_finish --- second phase of inplace update
894 : : *
895 : : * The tuple cannot change size, and therefore its header fields and null
896 : : * bitmap (if any) don't change either.
897 : : */
898 : : void
899 : 79174 : systable_inplace_update_finish(void *state, HeapTuple tuple)
900 : : {
901 : 79174 : SysScanDesc scan = (SysScanDesc) state;
902 : 79174 : Relation relation = scan->heap_rel;
903 : 79174 : TupleTableSlot *slot = scan->slot;
904 : 79174 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
905 : 79174 : HeapTuple oldtup = bslot->base.tuple;
906 : 79174 : Buffer buffer = bslot->buffer;
907 : :
908 : 79174 : heap_inplace_update_and_unlock(relation, oldtup, tuple, buffer);
909 : 79174 : systable_endscan(scan);
910 : 79174 : }
911 : :
912 : : /*
913 : : * systable_inplace_update_cancel --- abandon inplace update
914 : : *
915 : : * This is an alternative to making a no-op update.
916 : : */
917 : : void
918 : 35142 : systable_inplace_update_cancel(void *state)
919 : : {
920 : 35142 : SysScanDesc scan = (SysScanDesc) state;
921 : 35142 : Relation relation = scan->heap_rel;
922 : 35142 : TupleTableSlot *slot = scan->slot;
923 : 35142 : BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
924 : 35142 : HeapTuple oldtup = bslot->base.tuple;
925 : 35142 : Buffer buffer = bslot->buffer;
926 : :
927 : 35142 : heap_inplace_unlock(relation, oldtup, buffer);
928 : 35142 : systable_endscan(scan);
929 : 35142 : }
|