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