Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * genam.c
4 : * general index access method routines
5 : *
6 : * Portions Copyright (c) 1996-2023, 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/builtins.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 : #include "utils/syscache.h"
40 :
41 :
42 : /* ----------------------------------------------------------------
43 : * general access method routines
44 : *
45 : * All indexed access methods use an identical scan structure.
46 : * We don't know how the various AMs do locking, however, so we don't
47 : * do anything about that here.
48 : *
49 : * The intent is that an AM implementor will define a beginscan routine
50 : * that calls RelationGetIndexScan, to fill in the scan, and then does
51 : * whatever kind of locking he wants.
52 : *
53 : * At the end of a scan, the AM's endscan routine undoes the locking,
54 : * but does *not* call IndexScanEnd --- the higher-level index_endscan
55 : * routine does that. (We can't do it in the AM because index_endscan
56 : * still needs to touch the IndexScanDesc after calling the AM.)
57 : *
58 : * Because of this, the AM does not have a choice whether to call
59 : * RelationGetIndexScan or not; its beginscan routine must return an
60 : * object made by RelationGetIndexScan. This is kinda ugly but not
61 : * worth cleaning up now.
62 : * ----------------------------------------------------------------
63 : */
64 :
65 : /* ----------------
66 : * RelationGetIndexScan -- Create and fill an IndexScanDesc.
67 : *
68 : * This routine creates an index scan structure and sets up initial
69 : * contents for it.
70 : *
71 : * Parameters:
72 : * indexRelation -- index relation for scan.
73 : * nkeys -- count of scan keys (index qual conditions).
74 : * norderbys -- count of index order-by operators.
75 : *
76 : * Returns:
77 : * An initialized IndexScanDesc.
78 : * ----------------
79 : */
80 : IndexScanDesc
4511 tgl 81 CBC 8905358 : RelationGetIndexScan(Relation indexRelation, int nkeys, int norderbys)
82 : {
83 : IndexScanDesc scan;
84 :
9345 bruce 85 8905358 : scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
86 :
7629 tgl 87 8905358 : scan->heapRelation = NULL; /* may be set later */
1490 andres 88 8905358 : scan->xs_heapfetch = NULL;
7629 tgl 89 8905358 : scan->indexRelation = indexRelation;
2118 90 8905358 : scan->xs_snapshot = InvalidSnapshot; /* caller must initialize this */
7629 91 8905358 : scan->numberOfKeys = nkeys;
4511 92 8905358 : scan->numberOfOrderBys = norderbys;
93 :
94 : /*
95 : * We allocate key workspace here, but it won't get filled until amrescan.
96 : */
7629 97 8905358 : if (nkeys > 0)
98 8900408 : scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
99 : else
100 4950 : scan->keyData = NULL;
4511 101 8905358 : if (norderbys > 0)
102 96 : scan->orderByData = (ScanKey) palloc(sizeof(ScanKeyData) * norderbys);
103 : else
104 8905262 : scan->orderByData = NULL;
105 :
3955 bruce 106 8905358 : scan->xs_want_itup = false; /* may be set later */
107 :
108 : /*
109 : * During recovery we ignore killed tuples and don't bother to kill them
110 : * either. We do this because the xmin on the primary node could easily be
111 : * later than the xmin on the standby node, so that what the primary
112 : * thinks is killed is supposed to be visible on standby. So for correct
113 : * MVCC for queries during recovery we must ignore these hints and check
114 : * all tuples. Do *not* set ignore_killed_tuples to true when running in a
115 : * transaction that was started during recovery. xactStartedInRecovery
116 : * should not be altered by index AMs.
117 : */
7625 tgl 118 8905358 : scan->kill_prior_tuple = false;
4859 simon 119 8905358 : scan->xactStartedInRecovery = TransactionStartedDuringRecovery();
120 8905358 : scan->ignore_killed_tuples = !scan->xactStartedInRecovery;
121 :
9345 bruce 122 8905358 : scan->opaque = NULL;
123 :
4202 tgl 124 8905358 : scan->xs_itup = NULL;
4193 125 8905358 : scan->xs_itupdesc = NULL;
2232 126 8905358 : scan->xs_hitup = NULL;
127 8905358 : scan->xs_hitupdesc = NULL;
128 :
8986 bruce 129 8905358 : return scan;
130 : }
131 :
132 : /* ----------------
133 : * IndexScanEnd -- End an index scan.
134 : *
135 : * This routine just releases the storage acquired by
136 : * RelationGetIndexScan(). Any AM-level resources are
137 : * assumed to already have been released by the AM's
138 : * endscan routine.
139 : *
140 : * Returns:
141 : * None.
142 : * ----------------
143 : */
144 : void
8501 tgl 145 8904646 : IndexScanEnd(IndexScanDesc scan)
146 : {
147 8904646 : if (scan->keyData != NULL)
148 8899758 : pfree(scan->keyData);
4511 149 8904646 : if (scan->orderByData != NULL)
150 93 : pfree(scan->orderByData);
151 :
8501 152 8904646 : pfree(scan);
153 8904646 : }
154 :
155 : /*
156 : * BuildIndexValueDescription
157 : *
158 : * Construct a string describing the contents of an index entry, in the
159 : * form "(key_name, ...)=(key_value, ...)". This is currently used
160 : * for building unique-constraint and exclusion-constraint error messages,
161 : * so only key columns of the index are checked and printed.
162 : *
163 : * Note that if the user does not have permissions to view all of the
164 : * columns involved then a NULL is returned. Returning a partial key seems
165 : * unlikely to be useful and we have no way to know which of the columns the
166 : * user provided (unlike in ExecBuildSlotValueDescription).
167 : *
168 : * The passed-in values/nulls arrays are the "raw" input to the index AM,
169 : * e.g. results of FormIndexDatum --- this is not necessarily what is stored
170 : * in the index, but it's what the user perceives to be stored.
171 : *
172 : * Note: if you change anything here, check whether
173 : * ExecBuildSlotPartitionKeyDescription() in execMain.c needs a similar
174 : * change.
175 : */
176 : char *
4999 177 562 : BuildIndexValueDescription(Relation indexRelation,
178 : Datum *values, bool *isnull)
179 : {
180 : StringInfoData buf;
181 : Form_pg_index idxrec;
182 : int indnkeyatts;
183 : int i;
184 : int keyno;
3009 sfrost 185 562 : Oid indexrelid = RelationGetRelid(indexRelation);
186 : Oid indrelid;
187 : AclResult aclresult;
188 :
1828 teodor 189 562 : indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRelation);
190 :
191 : /*
192 : * Check permissions- if the user does not have access to view all of the
193 : * key columns then return NULL to avoid leaking data.
194 : *
195 : * First check if RLS is enabled for the relation. If so, return NULL to
196 : * avoid leaking data.
197 : *
198 : * Next we need to check table-level SELECT access and then, if there is
199 : * no access there, check column-level permissions.
200 : */
1728 alvherre 201 562 : idxrec = indexRelation->rd_index;
3009 sfrost 202 562 : indrelid = idxrec->indrelid;
203 562 : Assert(indexrelid == idxrec->indexrelid);
204 :
205 : /* RLS check- if RLS is enabled then we don't return anything. */
2812 mail 206 562 : if (check_enable_rls(indrelid, InvalidOid, true) == RLS_ENABLED)
3009 sfrost 207 6 : return NULL;
208 :
209 : /* Table-level SELECT is enough, if the user has it */
210 556 : aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT);
211 556 : if (aclresult != ACLCHECK_OK)
212 : {
213 : /*
214 : * No table-level access, so step through the columns in the index and
215 : * make sure the user has SELECT rights on all of them.
216 : */
1728 alvherre 217 12 : for (keyno = 0; keyno < indnkeyatts; keyno++)
218 : {
3009 sfrost 219 12 : AttrNumber attnum = idxrec->indkey.values[keyno];
220 :
221 : /*
222 : * Note that if attnum == InvalidAttrNumber, then this is an index
223 : * based on an expression and we return no detail rather than try
224 : * to figure out what column(s) the expression includes and if the
225 : * user has SELECT rights on them.
226 : */
2992 227 24 : if (attnum == InvalidAttrNumber ||
228 12 : pg_attribute_aclcheck(indrelid, attnum, GetUserId(),
229 : ACL_SELECT) != ACLCHECK_OK)
230 : {
231 : /* No access, so clean up and return */
3009 232 6 : return NULL;
233 : }
234 : }
235 : }
236 :
4999 tgl 237 550 : initStringInfo(&buf);
238 550 : appendStringInfo(&buf, "(%s)=(",
239 : pg_get_indexdef_columns(indexrelid, true));
240 :
1828 teodor 241 1167 : for (i = 0; i < indnkeyatts; i++)
242 : {
243 : char *val;
244 :
4999 tgl 245 617 : if (isnull[i])
246 9 : val = "null";
247 : else
248 : {
249 : Oid foutoid;
250 : bool typisvarlena;
251 :
252 : /*
253 : * The provided data is not necessarily of the type stored in the
254 : * index; rather it is of the index opclass's input type. So look
255 : * at rd_opcintype not the index tupdesc.
256 : *
257 : * Note: this is a bit shaky for opclasses that have pseudotype
258 : * input types such as ANYARRAY or RECORD. Currently, the
259 : * typoutput functions associated with the pseudotypes will work
260 : * okay, but we might have to try harder in future.
261 : */
4871 262 608 : getTypeOutputInfo(indexRelation->rd_opcintype[i],
263 : &foutoid, &typisvarlena);
4999 264 608 : val = OidOutputFunctionCall(foutoid, values[i]);
265 : }
266 :
267 617 : if (i > 0)
268 67 : appendStringInfoString(&buf, ", ");
269 617 : appendStringInfoString(&buf, val);
270 : }
271 :
272 550 : appendStringInfoChar(&buf, ')');
273 :
274 550 : return buf.data;
275 : }
276 :
277 : /*
278 : * Get the snapshotConflictHorizon from the table entries pointed to by the
279 : * index tuples being deleted using an AM-generic approach.
280 : *
281 : * This is a table_index_delete_tuples() shim used by index AMs that only need
282 : * to consult the tableam to get a snapshotConflictHorizon value, and only
283 : * expect to delete index tuples that are already known deletable (typically
284 : * due to having LP_DEAD bits set). When a snapshotConflictHorizon value
285 : * isn't needed in index AM's deletion WAL record, it is safe for it to skip
286 : * calling here entirely.
287 : *
288 : * We assume that caller index AM uses the standard IndexTuple representation,
289 : * with table TIDs stored in the t_tid field. We also expect (and assert)
290 : * that the line pointers on page for 'itemnos' offsets are already marked
291 : * LP_DEAD.
292 : */
293 : TransactionId
1475 andres 294 UIC 0 : index_compute_xid_horizon_for_tuples(Relation irel,
1475 andres 295 EUB : Relation hrel,
296 : Buffer ibuf,
297 : OffsetNumber *itemnos,
298 : int nitems)
299 : {
300 : TM_IndexDeleteOp delstate;
143 pg 301 UNC 0 : TransactionId snapshotConflictHorizon = InvalidTransactionId;
1475 andres 302 UBC 0 : Page ipage = BufferGetPage(ibuf);
1475 andres 303 EUB : IndexTuple itup;
304 :
803 pg 305 UIC 0 : Assert(nitems > 0);
803 pg 306 EUB :
521 pg 307 UIC 0 : delstate.irel = irel;
521 pg 308 UBC 0 : delstate.iblknum = BufferGetBlockNumber(ibuf);
816 309 0 : delstate.bottomup = false;
310 0 : delstate.bottomupfreespace = 0;
311 0 : delstate.ndeltids = 0;
312 0 : delstate.deltids = palloc(nitems * sizeof(TM_IndexDelete));
313 0 : delstate.status = palloc(nitems * sizeof(TM_IndexStatus));
816 pg 314 EUB :
315 : /* identify what the index tuples about to be deleted point to */
1475 andres 316 UIC 0 : for (int i = 0; i < nitems; i++)
1475 andres 317 EUB : {
521 pg 318 UIC 0 : OffsetNumber offnum = itemnos[i];
1475 andres 319 EUB : ItemId iitemid;
320 :
521 pg 321 UIC 0 : iitemid = PageGetItemId(ipage, offnum);
1475 andres 322 UBC 0 : itup = (IndexTuple) PageGetItem(ipage, iitemid);
1475 andres 323 EUB :
816 pg 324 UIC 0 : Assert(ItemIdIsDead(iitemid));
816 pg 325 EUB :
816 pg 326 UIC 0 : ItemPointerCopy(&itup->t_tid, &delstate.deltids[i].tid);
816 pg 327 UBC 0 : delstate.deltids[i].id = delstate.ndeltids;
521 328 0 : delstate.status[i].idxoffnum = offnum;
816 329 0 : delstate.status[i].knowndeletable = true; /* LP_DEAD-marked */
330 0 : delstate.status[i].promising = false; /* unused */
331 0 : delstate.status[i].freespace = 0; /* unused */
816 pg 332 EUB :
816 pg 333 UIC 0 : delstate.ndeltids++;
1475 andres 334 EUB : }
335 :
336 : /* determine the actual xid horizon */
143 pg 337 UNC 0 : snapshotConflictHorizon = table_index_delete_tuples(hrel, &delstate);
816 pg 338 EUB :
339 : /* assert tableam agrees that all items are deletable */
816 pg 340 UIC 0 : Assert(delstate.ndeltids == nitems);
1475 andres 341 EUB :
816 pg 342 UIC 0 : pfree(delstate.deltids);
816 pg 343 UBC 0 : pfree(delstate.status);
1475 andres 344 EUB :
143 pg 345 UNC 0 : return snapshotConflictHorizon;
1475 andres 346 EUB : }
347 :
348 :
349 : /* ----------------------------------------------------------------
350 : * heap-or-index-scan access to system catalogs
351 : *
352 : * These functions support system catalog accesses that normally use
353 : * an index but need to be capable of being switched to heap scans
354 : * if the system indexes are unavailable.
355 : *
356 : * The specified scan keys must be compatible with the named index.
357 : * Generally this means that they must constrain either all columns
358 : * of the index, or the first K columns of an N-column index.
359 : *
360 : * These routines could work with non-system tables, actually,
361 : * but they're only useful when there is a known index to use with
362 : * the given scan keys; so in practice they're only good for
363 : * predetermined types of scans of system catalogs.
364 : * ----------------------------------------------------------------
365 : */
366 :
367 : /*
368 : * systable_beginscan --- set up for heap-or-index scan
369 : *
370 : * rel: catalog to scan, already opened and suitably locked
371 : * indexId: OID of index to conditionally use
372 : * indexOK: if false, forces a heap scan (see notes below)
373 : * snapshot: time qual to use (NULL for a recent catalog snapshot)
374 : * nkeys, key: scan keys
375 : *
376 : * The attribute numbers in the scan key should be set for the heap case.
377 : * If we choose to index, we reset them to 1..n to reference the index
378 : * columns. Note this means there must be one scankey qualification per
379 : * index column! This is checked by the Asserts in the normal, index-using
380 : * case, but won't be checked if the heapscan path is taken.
381 : *
382 : * The routine checks the normal cases for whether an indexscan is safe,
383 : * but caller can make additional checks and pass indexOK=false if needed.
384 : * In standard case indexOK can simply be constant TRUE.
385 : */
386 : SysScanDesc
7629 tgl 387 GIC 9189314 : systable_beginscan(Relation heapRelation,
6569 tgl 388 ECB : Oid indexId,
389 : bool indexOK,
390 : Snapshot snapshot,
391 : int nkeys, ScanKey key)
392 : {
393 : SysScanDesc sysscan;
394 : Relation irel;
395 :
6569 tgl 396 GIC 9189314 : if (indexOK &&
6303 peter_e 397 CBC 8947477 : !IgnoreSystemIndexes &&
6569 tgl 398 8667487 : !ReindexIsProcessingIndex(indexId))
6096 399 8663012 : irel = index_open(indexId, AccessShareLock);
7137 tgl 400 ECB : else
7137 tgl 401 GIC 526302 : irel = NULL;
7719 tgl 402 ECB :
7719 tgl 403 GIC 9189310 : sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
7629 tgl 404 ECB :
7629 tgl 405 GIC 9189310 : sysscan->heap_rel = heapRelation;
7137 tgl 406 CBC 9189310 : sysscan->irel = irel;
1490 andres 407 9189310 : sysscan->slot = table_slot_create(heapRelation, NULL);
7719 tgl 408 ECB :
3568 rhaas 409 GIC 9189310 : if (snapshot == NULL)
3568 rhaas 410 ECB : {
3260 bruce 411 GIC 8073730 : Oid relid = RelationGetRelid(heapRelation);
3568 rhaas 412 ECB :
3568 rhaas 413 GIC 8073730 : snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
3568 rhaas 414 CBC 8073730 : sysscan->snapshot = snapshot;
3568 rhaas 415 ECB : }
416 : else
417 : {
418 : /* Caller is responsible for any snapshot. */
3568 rhaas 419 GIC 1115580 : sysscan->snapshot = NULL;
3568 rhaas 420 ECB : }
421 :
7137 tgl 422 GIC 9189310 : if (irel)
7719 tgl 423 ECB : {
424 : int i;
425 :
426 : /* Change attribute numbers to be index column numbers. */
7719 tgl 427 GIC 24125029 : for (i = 0; i < nkeys; i++)
7719 tgl 428 ECB : {
429 : int j;
430 :
1828 teodor 431 GIC 24247050 : for (j = 0; j < IndexRelationGetNumberOfAttributes(irel); j++)
5267 heikki.linnakangas 432 ECB : {
5267 heikki.linnakangas 433 GIC 24247050 : if (key[i].sk_attno == irel->rd_index->indkey.values[j])
5267 heikki.linnakangas 434 ECB : {
5267 heikki.linnakangas 435 GIC 15462021 : key[i].sk_attno = j + 1;
5267 heikki.linnakangas 436 CBC 15462021 : break;
5267 heikki.linnakangas 437 ECB : }
438 : }
1828 teodor 439 GIC 15462021 : if (j == IndexRelationGetNumberOfAttributes(irel))
5267 heikki.linnakangas 440 LBC 0 : elog(ERROR, "column is not in index");
7719 tgl 441 EUB : }
442 :
6096 tgl 443 GIC 8663008 : sysscan->iscan = index_beginscan(heapRelation, irel,
4511 tgl 444 ECB : snapshot, nkeys, 0);
4511 tgl 445 GIC 8663008 : index_rescan(sysscan->iscan, key, nkeys, NULL, 0);
7719 tgl 446 CBC 8663008 : sysscan->scan = NULL;
7719 tgl 447 ECB : }
448 : else
449 : {
450 : /*
451 : * We disallow synchronized scans when forced to use a heapscan on a
452 : * catalog. In most cases the desired rows are near the front, so
453 : * that the unpredictable start point of a syncscan is a serious
454 : * disadvantage; and there are no compensating advantages, because
455 : * it's unlikely that such scans will occur in parallel.
456 : */
1490 andres 457 GIC 526302 : sysscan->scan = table_beginscan_strat(heapRelation, snapshot,
1490 andres 458 ECB : nkeys, key,
459 : true, false);
7719 tgl 460 GIC 526302 : sysscan->iscan = NULL;
7719 tgl 461 ECB : }
462 :
463 : /*
464 : * If CheckXidAlive is set then set a flag to indicate that system table
465 : * scan is in-progress. See detailed comments in xact.c where these
466 : * variables are declared.
467 : */
974 akapila 468 GIC 9189310 : if (TransactionIdIsValid(CheckXidAlive))
974 akapila 469 CBC 672 : bsysscan = true;
974 akapila 470 ECB :
7719 tgl 471 GIC 9189310 : return sysscan;
7719 tgl 472 ECB : }
473 :
474 : /*
475 : * HandleConcurrentAbort - Handle concurrent abort of the CheckXidAlive.
476 : *
477 : * Error out, if CheckXidAlive is aborted. We can't directly use
478 : * TransactionIdDidAbort as after crash such transaction might not have been
479 : * marked as aborted. See detailed comments in xact.c where the variable
480 : * is declared.
481 : */
482 : static inline void
974 akapila 483 GIC 19811170 : HandleConcurrentAbort()
974 akapila 484 ECB : {
974 akapila 485 GIC 19811170 : if (TransactionIdIsValid(CheckXidAlive) &&
974 akapila 486 CBC 980 : !TransactionIdIsInProgress(CheckXidAlive) &&
487 7 : !TransactionIdDidCommit(CheckXidAlive))
488 7 : ereport(ERROR,
974 akapila 489 ECB : (errcode(ERRCODE_TRANSACTION_ROLLBACK),
490 : errmsg("transaction aborted during system catalog scan")));
974 akapila 491 GIC 19811163 : }
974 akapila 492 ECB :
493 : /*
494 : * systable_getnext --- get next tuple in a heap-or-index scan
495 : *
496 : * Returns NULL if no more tuples available.
497 : *
498 : * Note that returned tuple is a reference to data in a disk buffer;
499 : * it must not be modified, and should be presumed inaccessible after
500 : * next getnext() or endscan() call.
501 : *
502 : * XXX: It'd probably make sense to offer a slot based interface, at least
503 : * optionally.
504 : */
505 : HeapTuple
7719 tgl 506 GIC 19486879 : systable_getnext(SysScanDesc sysscan)
7719 tgl 507 ECB : {
1490 andres 508 GIC 19486879 : HeapTuple htup = NULL;
7719 tgl 509 ECB :
7719 tgl 510 GIC 19486879 : if (sysscan->irel)
5474 tgl 511 ECB : {
1490 andres 512 GIC 17708275 : if (index_getnext_slot(sysscan->iscan, ForwardScanDirection, sysscan->slot))
1490 andres 513 ECB : {
514 : bool shouldFree;
515 :
1490 andres 516 GIC 12749633 : htup = ExecFetchSlotHeapTuple(sysscan->slot, false, &shouldFree);
1490 andres 517 CBC 12749633 : Assert(!shouldFree);
1490 andres 518 ECB :
519 : /*
520 : * We currently don't need to support lossy index operators for
521 : * any system catalog scan. It could be done here, using the scan
522 : * keys to drive the operator calls, if we arranged to save the
523 : * heap attnums during systable_beginscan(); this is practical
524 : * because we still wouldn't need to support indexes on
525 : * expressions.
526 : */
1490 andres 527 GIC 12749633 : if (sysscan->iscan->xs_recheck)
1490 andres 528 LBC 0 : elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
1490 andres 529 EUB : }
530 : }
531 : else
532 : {
1490 andres 533 GIC 1778604 : if (table_scan_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot))
1490 andres 534 ECB : {
535 : bool shouldFree;
536 :
1490 andres 537 GIC 1597300 : htup = ExecFetchSlotHeapTuple(sysscan->slot, false, &shouldFree);
1490 andres 538 CBC 1597300 : Assert(!shouldFree);
1490 andres 539 ECB : }
540 : }
541 :
542 : /*
543 : * Handle the concurrent abort while fetching the catalog tuple during
544 : * logical streaming of a transaction.
545 : */
974 akapila 546 GIC 19486878 : HandleConcurrentAbort();
974 akapila 547 ECB :
7719 tgl 548 GIC 19486871 : return htup;
7719 tgl 549 ECB : }
550 :
551 : /*
552 : * systable_recheck_tuple --- recheck visibility of most-recently-fetched tuple
553 : *
554 : * In particular, determine if this tuple would be visible to a catalog scan
555 : * that started now. We don't handle the case of a non-MVCC scan snapshot,
556 : * because no caller needs that yet.
557 : *
558 : * This is useful to test whether an object was deleted while we waited to
559 : * acquire lock on it.
560 : *
561 : * Note: we don't actually *need* the tuple to be passed in, but it's a
562 : * good crosscheck that the caller is interested in the right tuple.
563 : */
564 : bool
5418 tgl 565 GIC 89536 : systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
5418 tgl 566 ECB : {
567 : Snapshot freshsnap;
568 : bool result;
569 :
1490 andres 570 GIC 89536 : Assert(tup == ExecFetchSlotHeapTuple(sysscan->slot, false, NULL));
1490 andres 571 ECB :
572 : /*
573 : * Trust that table_tuple_satisfies_snapshot() and its subsidiaries
574 : * (commonly LockBuffer() and HeapTupleSatisfiesMVCC()) do not themselves
575 : * acquire snapshots, so we need not register the snapshot. Those
576 : * facilities are too low-level to have any business scanning tables.
577 : */
3554 noah 578 GIC 89536 : freshsnap = GetCatalogSnapshot(RelationGetRelid(sysscan->heap_rel));
3554 noah 579 ECB :
1490 andres 580 GIC 89536 : result = table_tuple_satisfies_snapshot(sysscan->heap_rel,
1490 andres 581 CBC 89536 : sysscan->slot,
1490 andres 582 ECB : freshsnap);
583 :
584 : /*
585 : * Handle the concurrent abort while fetching the catalog tuple during
586 : * logical streaming of a transaction.
587 : */
974 akapila 588 GIC 89536 : HandleConcurrentAbort();
974 akapila 589 ECB :
5418 tgl 590 GIC 89536 : return result;
5418 tgl 591 ECB : }
592 :
593 : /*
594 : * systable_endscan --- close scan, release resources
595 : *
596 : * Note that it's still up to the caller to close the heap relation.
597 : */
598 : void
7719 tgl 599 GIC 9189046 : systable_endscan(SysScanDesc sysscan)
7719 tgl 600 ECB : {
1490 andres 601 GIC 9189046 : if (sysscan->slot)
1490 andres 602 ECB : {
1490 andres 603 GIC 9189046 : ExecDropSingleTupleTableSlot(sysscan->slot);
1490 andres 604 CBC 9189046 : sysscan->slot = NULL;
1490 andres 605 ECB : }
606 :
7719 tgl 607 GIC 9189046 : if (sysscan->irel)
7719 tgl 608 ECB : {
7719 tgl 609 GIC 8662751 : index_endscan(sysscan->iscan);
6096 tgl 610 CBC 8662751 : index_close(sysscan->irel, AccessShareLock);
7719 tgl 611 ECB : }
612 : else
1490 andres 613 GIC 526295 : table_endscan(sysscan->scan);
7719 tgl 614 ECB :
3568 rhaas 615 GIC 9189046 : if (sysscan->snapshot)
3568 rhaas 616 CBC 8073466 : UnregisterSnapshot(sysscan->snapshot);
3568 rhaas 617 ECB :
618 : /*
619 : * Reset the bsysscan flag at the end of the systable scan. See detailed
620 : * comments in xact.c where these variables are declared.
621 : */
974 akapila 622 GIC 9189046 : if (TransactionIdIsValid(CheckXidAlive))
974 akapila 623 CBC 665 : bsysscan = false;
974 akapila 624 ECB :
7719 tgl 625 GIC 9189046 : pfree(sysscan);
7719 tgl 626 CBC 9189046 : }
5475 tgl 627 ECB :
628 :
629 : /*
630 : * systable_beginscan_ordered --- set up for ordered catalog scan
631 : *
632 : * These routines have essentially the same API as systable_beginscan etc,
633 : * except that they guarantee to return multiple matching tuples in
634 : * index order. Also, for largely historical reasons, the index to use
635 : * is opened and locked by the caller, not here.
636 : *
637 : * Currently we do not support non-index-based scans here. (In principle
638 : * we could do a heapscan and sort, but the uses are in places that
639 : * probably don't need to still work with corrupted catalog indexes.)
640 : * For the moment, therefore, these functions are merely the thinest of
641 : * wrappers around index_beginscan/index_getnext_slot. The main reason for
642 : * their existence is to centralize possible future support of lossy operators
643 : * in catalog scans.
644 : */
645 : SysScanDesc
5475 tgl 646 GIC 55407 : systable_beginscan_ordered(Relation heapRelation,
5475 tgl 647 ECB : Relation indexRelation,
648 : Snapshot snapshot,
649 : int nkeys, ScanKey key)
650 : {
651 : SysScanDesc sysscan;
652 : int i;
653 :
654 : /* REINDEX can probably be a hard error here ... */
5475 tgl 655 GIC 55407 : if (ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))
4809 tgl 656 LBC 0 : elog(ERROR, "cannot do ordered scan on index \"%s\", because it is being reindexed",
5475 tgl 657 EUB : RelationGetRelationName(indexRelation));
658 : /* ... but we only throw a warning about violating IgnoreSystemIndexes */
5475 tgl 659 GIC 55407 : if (IgnoreSystemIndexes)
5475 tgl 660 LBC 0 : elog(WARNING, "using index \"%s\" despite IgnoreSystemIndexes",
5475 tgl 661 EUB : RelationGetRelationName(indexRelation));
662 :
5475 tgl 663 GIC 55407 : sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
5475 tgl 664 ECB :
5475 tgl 665 GIC 55407 : sysscan->heap_rel = heapRelation;
5475 tgl 666 CBC 55407 : sysscan->irel = indexRelation;
1490 andres 667 55407 : sysscan->slot = table_slot_create(heapRelation, NULL);
5475 tgl 668 ECB :
3568 rhaas 669 GIC 55407 : if (snapshot == NULL)
3568 rhaas 670 ECB : {
3260 bruce 671 GIC 3799 : Oid relid = RelationGetRelid(heapRelation);
3568 rhaas 672 ECB :
3568 rhaas 673 GIC 3799 : snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
3568 rhaas 674 CBC 3799 : sysscan->snapshot = snapshot;
3568 rhaas 675 ECB : }
676 : else
677 : {
678 : /* Caller is responsible for any snapshot. */
3568 rhaas 679 GIC 51608 : sysscan->snapshot = NULL;
3568 rhaas 680 ECB : }
681 :
682 : /* Change attribute numbers to be index column numbers. */
5475 tgl 683 GIC 109569 : for (i = 0; i < nkeys; i++)
5475 tgl 684 ECB : {
685 : int j;
686 :
1828 teodor 687 GIC 55786 : for (j = 0; j < IndexRelationGetNumberOfAttributes(indexRelation); j++)
5267 heikki.linnakangas 688 ECB : {
5267 heikki.linnakangas 689 GIC 55786 : if (key[i].sk_attno == indexRelation->rd_index->indkey.values[j])
5267 heikki.linnakangas 690 ECB : {
5267 heikki.linnakangas 691 GIC 54162 : key[i].sk_attno = j + 1;
5267 heikki.linnakangas 692 CBC 54162 : break;
5267 heikki.linnakangas 693 ECB : }
694 : }
1828 teodor 695 GIC 54162 : if (j == IndexRelationGetNumberOfAttributes(indexRelation))
5267 heikki.linnakangas 696 LBC 0 : elog(ERROR, "column is not in index");
5475 tgl 697 EUB : }
698 :
5475 tgl 699 GIC 55407 : sysscan->iscan = index_beginscan(heapRelation, indexRelation,
4511 tgl 700 ECB : snapshot, nkeys, 0);
4511 tgl 701 GIC 55407 : index_rescan(sysscan->iscan, key, nkeys, NULL, 0);
5475 tgl 702 CBC 55407 : sysscan->scan = NULL;
5475 tgl 703 ECB :
5475 tgl 704 GIC 55407 : return sysscan;
5475 tgl 705 ECB : }
706 :
707 : /*
708 : * systable_getnext_ordered --- get next tuple in an ordered catalog scan
709 : */
710 : HeapTuple
5475 tgl 711 GIC 234759 : systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
5475 tgl 712 ECB : {
1490 andres 713 GIC 234759 : HeapTuple htup = NULL;
5475 tgl 714 ECB :
5475 tgl 715 GIC 234759 : Assert(sysscan->irel);
1490 andres 716 CBC 234759 : if (index_getnext_slot(sysscan->iscan, direction, sysscan->slot))
717 179970 : htup = ExecFetchSlotHeapTuple(sysscan->slot, false, NULL);
1490 andres 718 ECB :
719 : /* See notes in systable_getnext */
5474 tgl 720 GIC 234756 : if (htup && sysscan->iscan->xs_recheck)
5474 tgl 721 LBC 0 : elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
5475 tgl 722 EUB :
723 : /*
724 : * Handle the concurrent abort while fetching the catalog tuple during
725 : * logical streaming of a transaction.
726 : */
974 akapila 727 GIC 234756 : HandleConcurrentAbort();
974 akapila 728 ECB :
5475 tgl 729 GIC 234756 : return htup;
5475 tgl 730 ECB : }
731 :
732 : /*
733 : * systable_endscan_ordered --- close scan, release resources
734 : */
735 : void
5475 tgl 736 GIC 55395 : systable_endscan_ordered(SysScanDesc sysscan)
5475 tgl 737 ECB : {
1490 andres 738 GIC 55395 : if (sysscan->slot)
1490 andres 739 ECB : {
1490 andres 740 GIC 55395 : ExecDropSingleTupleTableSlot(sysscan->slot);
1490 andres 741 CBC 55395 : sysscan->slot = NULL;
1490 andres 742 ECB : }
743 :
5475 tgl 744 GIC 55395 : Assert(sysscan->irel);
5475 tgl 745 CBC 55395 : index_endscan(sysscan->iscan);
3568 rhaas 746 55395 : if (sysscan->snapshot)
747 3790 : UnregisterSnapshot(sysscan->snapshot);
5475 tgl 748 55395 : pfree(sysscan);
749 55395 : }
|