Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * relcache.c
4 : : * POSTGRES relation descriptor cache code
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/utils/cache/relcache.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : /*
16 : : * INTERFACE ROUTINES
17 : : * RelationCacheInitialize - initialize relcache (to empty)
18 : : * RelationCacheInitializePhase2 - initialize shared-catalog entries
19 : : * RelationCacheInitializePhase3 - finish initializing relcache
20 : : * RelationIdGetRelation - get a reldesc by relation id
21 : : * RelationClose - close an open relation
22 : : *
23 : : * NOTES
24 : : * The following code contains many undocumented hacks. Please be
25 : : * careful....
26 : : */
27 : : #include "postgres.h"
28 : :
29 : : #include <sys/file.h>
30 : : #include <fcntl.h>
31 : : #include <unistd.h>
32 : :
33 : : #include "access/htup_details.h"
34 : : #include "access/multixact.h"
35 : : #include "access/parallel.h"
36 : : #include "access/reloptions.h"
37 : : #include "access/sysattr.h"
38 : : #include "access/table.h"
39 : : #include "access/tableam.h"
40 : : #include "access/tupdesc_details.h"
41 : : #include "access/xact.h"
42 : : #include "catalog/binary_upgrade.h"
43 : : #include "catalog/catalog.h"
44 : : #include "catalog/indexing.h"
45 : : #include "catalog/namespace.h"
46 : : #include "catalog/partition.h"
47 : : #include "catalog/pg_am.h"
48 : : #include "catalog/pg_amproc.h"
49 : : #include "catalog/pg_attrdef.h"
50 : : #include "catalog/pg_auth_members.h"
51 : : #include "catalog/pg_authid.h"
52 : : #include "catalog/pg_constraint.h"
53 : : #include "catalog/pg_database.h"
54 : : #include "catalog/pg_namespace.h"
55 : : #include "catalog/pg_opclass.h"
56 : : #include "catalog/pg_proc.h"
57 : : #include "catalog/pg_publication.h"
58 : : #include "catalog/pg_rewrite.h"
59 : : #include "catalog/pg_shseclabel.h"
60 : : #include "catalog/pg_statistic_ext.h"
61 : : #include "catalog/pg_subscription.h"
62 : : #include "catalog/pg_tablespace.h"
63 : : #include "catalog/pg_trigger.h"
64 : : #include "catalog/pg_type.h"
65 : : #include "catalog/schemapg.h"
66 : : #include "catalog/storage.h"
67 : : #include "commands/policy.h"
68 : : #include "commands/publicationcmds.h"
69 : : #include "commands/trigger.h"
70 : : #include "common/int.h"
71 : : #include "miscadmin.h"
72 : : #include "nodes/makefuncs.h"
73 : : #include "nodes/nodeFuncs.h"
74 : : #include "optimizer/optimizer.h"
75 : : #include "pgstat.h"
76 : : #include "rewrite/rewriteDefine.h"
77 : : #include "rewrite/rowsecurity.h"
78 : : #include "storage/lmgr.h"
79 : : #include "storage/smgr.h"
80 : : #include "utils/array.h"
81 : : #include "utils/builtins.h"
82 : : #include "utils/catcache.h"
83 : : #include "utils/datum.h"
84 : : #include "utils/fmgroids.h"
85 : : #include "utils/inval.h"
86 : : #include "utils/lsyscache.h"
87 : : #include "utils/memutils.h"
88 : : #include "utils/relmapper.h"
89 : : #include "utils/resowner.h"
90 : : #include "utils/snapmgr.h"
91 : : #include "utils/syscache.h"
92 : :
93 : : #define RELCACHE_INIT_FILEMAGIC 0x573266 /* version ID value */
94 : :
95 : : /*
96 : : * Whether to bother checking if relation cache memory needs to be freed
97 : : * eagerly. See also RelationBuildDesc() and pg_config_manual.h.
98 : : */
99 : : #if defined(RECOVER_RELATION_BUILD_MEMORY) && (RECOVER_RELATION_BUILD_MEMORY != 0)
100 : : #define MAYBE_RECOVER_RELATION_BUILD_MEMORY 1
101 : : #else
102 : : #define RECOVER_RELATION_BUILD_MEMORY 0
103 : : #ifdef DISCARD_CACHES_ENABLED
104 : : #define MAYBE_RECOVER_RELATION_BUILD_MEMORY 1
105 : : #endif
106 : : #endif
107 : :
108 : : /*
109 : : * hardcoded tuple descriptors, contents generated by genbki.pl
110 : : */
111 : : static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
112 : : static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
113 : : static const FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
114 : : static const FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
115 : : static const FormData_pg_attribute Desc_pg_database[Natts_pg_database] = {Schema_pg_database};
116 : : static const FormData_pg_attribute Desc_pg_authid[Natts_pg_authid] = {Schema_pg_authid};
117 : : static const FormData_pg_attribute Desc_pg_auth_members[Natts_pg_auth_members] = {Schema_pg_auth_members};
118 : : static const FormData_pg_attribute Desc_pg_index[Natts_pg_index] = {Schema_pg_index};
119 : : static const FormData_pg_attribute Desc_pg_shseclabel[Natts_pg_shseclabel] = {Schema_pg_shseclabel};
120 : : static const FormData_pg_attribute Desc_pg_subscription[Natts_pg_subscription] = {Schema_pg_subscription};
121 : :
122 : : /*
123 : : * Hash tables that index the relation cache
124 : : *
125 : : * We used to index the cache by both name and OID, but now there
126 : : * is only an index by OID.
127 : : */
128 : : typedef struct relidcacheent
129 : : {
130 : : Oid reloid;
131 : : Relation reldesc;
132 : : } RelIdCacheEnt;
133 : :
134 : : static HTAB *RelationIdCache;
135 : :
136 : : /*
137 : : * This flag is false until we have prepared the critical relcache entries
138 : : * that are needed to do indexscans on the tables read by relcache building.
139 : : */
140 : : bool criticalRelcachesBuilt = false;
141 : :
142 : : /*
143 : : * This flag is false until we have prepared the critical relcache entries
144 : : * for shared catalogs (which are the tables needed for login).
145 : : */
146 : : bool criticalSharedRelcachesBuilt = false;
147 : :
148 : : /*
149 : : * This counter counts relcache inval events received since backend startup
150 : : * (but only for rels that are actually in cache). Presently, we use it only
151 : : * to detect whether data about to be written by write_relcache_init_file()
152 : : * might already be obsolete.
153 : : */
154 : : static long relcacheInvalsReceived = 0L;
155 : :
156 : : /*
157 : : * in_progress_list is a stack of ongoing RelationBuildDesc() calls. CREATE
158 : : * INDEX CONCURRENTLY makes catalog changes under ShareUpdateExclusiveLock.
159 : : * It critically relies on each backend absorbing those changes no later than
160 : : * next transaction start. Hence, RelationBuildDesc() loops until it finishes
161 : : * without accepting a relevant invalidation. (Most invalidation consumers
162 : : * don't do this.)
163 : : */
164 : : typedef struct inprogressent
165 : : {
166 : : Oid reloid; /* OID of relation being built */
167 : : bool invalidated; /* whether an invalidation arrived for it */
168 : : } InProgressEnt;
169 : :
170 : : static InProgressEnt *in_progress_list;
171 : : static int in_progress_list_len;
172 : : static int in_progress_list_maxlen;
173 : :
174 : : /*
175 : : * eoxact_list[] stores the OIDs of relations that (might) need AtEOXact
176 : : * cleanup work. This list intentionally has limited size; if it overflows,
177 : : * we fall back to scanning the whole hashtable. There is no value in a very
178 : : * large list because (1) at some point, a hash_seq_search scan is faster than
179 : : * retail lookups, and (2) the value of this is to reduce EOXact work for
180 : : * short transactions, which can't have dirtied all that many tables anyway.
181 : : * EOXactListAdd() does not bother to prevent duplicate list entries, so the
182 : : * cleanup processing must be idempotent.
183 : : */
184 : : #define MAX_EOXACT_LIST 32
185 : : static Oid eoxact_list[MAX_EOXACT_LIST];
186 : : static int eoxact_list_len = 0;
187 : : static bool eoxact_list_overflowed = false;
188 : :
189 : : #define EOXactListAdd(rel) \
190 : : do { \
191 : : if (eoxact_list_len < MAX_EOXACT_LIST) \
192 : : eoxact_list[eoxact_list_len++] = (rel)->rd_id; \
193 : : else \
194 : : eoxact_list_overflowed = true; \
195 : : } while (0)
196 : :
197 : : /*
198 : : * EOXactTupleDescArray stores TupleDescs that (might) need AtEOXact
199 : : * cleanup work. The array expands as needed; there is no hashtable because
200 : : * we don't need to access individual items except at EOXact.
201 : : */
202 : : static TupleDesc *EOXactTupleDescArray;
203 : : static int NextEOXactTupleDescNum = 0;
204 : : static int EOXactTupleDescArrayLen = 0;
205 : :
206 : : /*
207 : : * macros to manipulate the lookup hashtable
208 : : */
209 : : #define RelationCacheInsert(RELATION, replace_allowed) \
210 : : do { \
211 : : RelIdCacheEnt *hentry; bool found; \
212 : : hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
213 : : &((RELATION)->rd_id), \
214 : : HASH_ENTER, &found); \
215 : : if (found) \
216 : : { \
217 : : /* see comments in RelationBuildDesc and RelationBuildLocalRelation */ \
218 : : Relation _old_rel = hentry->reldesc; \
219 : : Assert(replace_allowed); \
220 : : hentry->reldesc = (RELATION); \
221 : : if (RelationHasReferenceCountZero(_old_rel)) \
222 : : RelationDestroyRelation(_old_rel, false); \
223 : : else if (!IsBootstrapProcessingMode()) \
224 : : elog(WARNING, "leaking still-referenced relcache entry for \"%s\"", \
225 : : RelationGetRelationName(_old_rel)); \
226 : : } \
227 : : else \
228 : : hentry->reldesc = (RELATION); \
229 : : } while(0)
230 : :
231 : : #define RelationIdCacheLookup(ID, RELATION) \
232 : : do { \
233 : : RelIdCacheEnt *hentry; \
234 : : hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
235 : : &(ID), \
236 : : HASH_FIND, NULL); \
237 : : if (hentry) \
238 : : RELATION = hentry->reldesc; \
239 : : else \
240 : : RELATION = NULL; \
241 : : } while(0)
242 : :
243 : : #define RelationCacheDelete(RELATION) \
244 : : do { \
245 : : RelIdCacheEnt *hentry; \
246 : : hentry = (RelIdCacheEnt *) hash_search(RelationIdCache, \
247 : : &((RELATION)->rd_id), \
248 : : HASH_REMOVE, NULL); \
249 : : if (hentry == NULL) \
250 : : elog(WARNING, "failed to delete relcache entry for OID %u", \
251 : : (RELATION)->rd_id); \
252 : : } while(0)
253 : :
254 : :
255 : : /*
256 : : * Special cache for opclass-related information
257 : : *
258 : : * Note: only default support procs get cached, ie, those with
259 : : * lefttype = righttype = opcintype.
260 : : */
261 : : typedef struct opclasscacheent
262 : : {
263 : : Oid opclassoid; /* lookup key: OID of opclass */
264 : : bool valid; /* set true after successful fill-in */
265 : : StrategyNumber numSupport; /* max # of support procs (from pg_am) */
266 : : Oid opcfamily; /* OID of opclass's family */
267 : : Oid opcintype; /* OID of opclass's declared input type */
268 : : RegProcedure *supportProcs; /* OIDs of support procedures */
269 : : } OpClassCacheEnt;
270 : :
271 : : static HTAB *OpClassCache = NULL;
272 : :
273 : :
274 : : /* non-export function prototypes */
275 : :
276 : : static void RelationCloseCleanup(Relation relation);
277 : : static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
278 : : static void RelationClearRelation(Relation relation, bool rebuild);
279 : :
280 : : static void RelationReloadIndexInfo(Relation relation);
281 : : static void RelationReloadNailed(Relation relation);
282 : : static void RelationFlushRelation(Relation relation);
283 : : static void RememberToFreeTupleDescAtEOX(TupleDesc td);
284 : : #ifdef USE_ASSERT_CHECKING
285 : : static void AssertPendingSyncConsistency(Relation relation);
286 : : #endif
287 : : static void AtEOXact_cleanup(Relation relation, bool isCommit);
288 : : static void AtEOSubXact_cleanup(Relation relation, bool isCommit,
289 : : SubTransactionId mySubid, SubTransactionId parentSubid);
290 : : static bool load_relcache_init_file(bool shared);
291 : : static void write_relcache_init_file(bool shared);
292 : : static void write_item(const void *data, Size len, FILE *fp);
293 : :
294 : : static void formrdesc(const char *relationName, Oid relationReltype,
295 : : bool isshared, int natts, const FormData_pg_attribute *attrs);
296 : :
297 : : static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_historic);
298 : : static Relation AllocateRelationDesc(Form_pg_class relp);
299 : : static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
300 : : static void RelationBuildTupleDesc(Relation relation);
301 : : static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
302 : : static void RelationInitPhysicalAddr(Relation relation);
303 : : static void load_critical_index(Oid indexoid, Oid heapoid);
304 : : static TupleDesc GetPgClassDescriptor(void);
305 : : static TupleDesc GetPgIndexDescriptor(void);
306 : : static void AttrDefaultFetch(Relation relation, int ndef);
307 : : static int AttrDefaultCmp(const void *a, const void *b);
308 : : static void CheckConstraintFetch(Relation relation);
309 : : static int CheckConstraintCmp(const void *a, const void *b);
310 : : static void InitIndexAmRoutine(Relation relation);
311 : : static void IndexSupportInitialize(oidvector *indclass,
312 : : RegProcedure *indexSupport,
313 : : Oid *opFamily,
314 : : Oid *opcInType,
315 : : StrategyNumber maxSupportNumber,
316 : : AttrNumber maxAttributeNumber);
317 : : static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
318 : : StrategyNumber numSupport);
319 : : static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
320 : : static void unlink_initfile(const char *initfilename, int elevel);
321 : :
322 : :
323 : : /*
324 : : * ScanPgRelation
325 : : *
326 : : * This is used by RelationBuildDesc to find a pg_class
327 : : * tuple matching targetRelId. The caller must hold at least
328 : : * AccessShareLock on the target relid to prevent concurrent-update
329 : : * scenarios; it isn't guaranteed that all scans used to build the
330 : : * relcache entry will use the same snapshot. If, for example,
331 : : * an attribute were to be added after scanning pg_class and before
332 : : * scanning pg_attribute, relnatts wouldn't match.
333 : : *
334 : : * NB: the returned tuple has been copied into palloc'd storage
335 : : * and must eventually be freed with heap_freetuple.
336 : : */
337 : : static HeapTuple
3695 rhaas@postgresql.org 338 :CBC 712800 : ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_historic)
339 : : {
340 : : HeapTuple pg_class_tuple;
341 : : Relation pg_class_desc;
342 : : SysScanDesc pg_class_scan;
343 : : ScanKeyData key[1];
1478 andres@anarazel.de 344 : 712800 : Snapshot snapshot = NULL;
345 : :
346 : : /*
347 : : * If something goes wrong during backend startup, we might find ourselves
348 : : * trying to read pg_class before we've selected a database. That ain't
349 : : * gonna work, so bail out with a useful error message. If this happens,
350 : : * it probably means a relcache entry that needs to be nailed isn't.
351 : : */
5359 tgl@sss.pgh.pa.us 352 [ - + ]: 712800 : if (!OidIsValid(MyDatabaseId))
5359 tgl@sss.pgh.pa.us 353 [ # # ]:UBC 0 : elog(FATAL, "cannot read pg_class without having selected a database");
354 : :
355 : : /*
356 : : * form a scan key
357 : : */
6940 tgl@sss.pgh.pa.us 358 :CBC 712800 : ScanKeyInit(&key[0],
359 : : Anum_pg_class_oid,
360 : : BTEqualStrategyNumber, F_OIDEQ,
361 : : ObjectIdGetDatum(targetRelId));
362 : :
363 : : /*
364 : : * Open pg_class and fetch a tuple. Force heap scan if we haven't yet
365 : : * built the critical relcache entries (this includes initdb and startup
366 : : * without a pg_internal.init file). The caller can also force a heap
367 : : * scan by setting indexOK == false.
368 : : */
1910 andres@anarazel.de 369 : 712800 : pg_class_desc = table_open(RelationRelationId, AccessShareLock);
370 : :
371 : : /*
372 : : * The caller might need a tuple that's newer than the one the historic
373 : : * snapshot; currently the only case requiring to do so is looking up the
374 : : * relfilenumber of non mapped system relations during decoding. That
375 : : * snapshot can't change in the midst of a relcache build, so there's no
376 : : * need to register the snapshot.
377 : : */
3695 rhaas@postgresql.org 378 [ + + ]: 712800 : if (force_non_historic)
379 : 1348 : snapshot = GetNonHistoricCatalogSnapshot(RelationRelationId);
380 : :
6940 tgl@sss.pgh.pa.us 381 : 712800 : pg_class_scan = systable_beginscan(pg_class_desc, ClassOidIndexId,
7508 382 [ + + + + ]: 712800 : indexOK && criticalRelcachesBuilt,
383 : : snapshot,
6940 tgl@sss.pgh.pa.us 384 :ECB (843472) : 1, key);
385 : :
8090 tgl@sss.pgh.pa.us 386 :CBC 712797 : pg_class_tuple = systable_getnext(pg_class_scan);
387 : :
388 : : /*
389 : : * Must copy tuple before releasing buffer.
390 : : */
391 [ + + ]: 712790 : if (HeapTupleIsValid(pg_class_tuple))
392 : 712119 : pg_class_tuple = heap_copytuple(pg_class_tuple);
393 : :
394 : : /* all done */
395 : 712790 : systable_endscan(pg_class_scan);
1910 andres@anarazel.de 396 : 712790 : table_close(pg_class_desc, AccessShareLock);
397 : :
8090 tgl@sss.pgh.pa.us 398 : 712790 : return pg_class_tuple;
399 : : }
400 : :
401 : : /*
402 : : * AllocateRelationDesc
403 : : *
404 : : * This is used to allocate memory for a new relation descriptor
405 : : * and initialize the rd_rel field from the given pg_class tuple.
406 : : */
407 : : static Relation
5206 408 : 665227 : AllocateRelationDesc(Form_pg_class relp)
409 : : {
410 : : Relation relation;
411 : : MemoryContext oldcxt;
412 : : Form_pg_class relationForm;
413 : :
414 : : /* Relcache entries must live in CacheMemoryContext */
8689 415 : 665227 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
416 : :
417 : : /*
418 : : * allocate and zero space for new relation descriptor
419 : : */
5206 420 : 665227 : relation = (Relation) palloc0(sizeof(RelationData));
421 : :
422 : : /* make sure relation is marked as having no open file yet */
7369 423 : 665227 : relation->rd_smgr = NULL;
424 : :
425 : : /*
426 : : * Copy the relation tuple form
427 : : *
428 : : * We only allocate space for the fixed fields, ie, CLASS_TUPLE_SIZE. The
429 : : * variable-length fields (relacl, reloptions) are NOT stored in the
430 : : * relcache --- there'd be little point in it, since we don't copy the
431 : : * tuple's nulls bitmap and hence wouldn't know if the values are valid.
432 : : * Bottom line is that relacl *cannot* be retrieved from the relcache. Get
433 : : * it from the syscache if you need it. The same goes for the original
434 : : * form of reloptions (however, we do store the parsed form of reloptions
435 : : * in rd_options).
436 : : */
8689 437 : 665227 : relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
438 : :
6913 neilc@samurai.com 439 : 665227 : memcpy(relationForm, relp, CLASS_TUPLE_SIZE);
440 : :
441 : : /* initialize relation tuple form */
9357 bruce@momjian.us 442 : 665227 : relation->rd_rel = relationForm;
443 : :
444 : : /* and allocate attribute tuple form storage */
1972 andres@anarazel.de 445 : 665227 : relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts);
446 : : /* which we mark as a reference-counted tupdesc */
6512 tgl@sss.pgh.pa.us 447 : 665227 : relation->rd_att->tdrefcount = 1;
448 : :
8689 449 : 665227 : MemoryContextSwitchTo(oldcxt);
450 : :
9716 bruce@momjian.us 451 : 665227 : return relation;
452 : : }
453 : :
454 : : /*
455 : : * RelationParseRelOptions
456 : : * Convert pg_class.reloptions into pre-parsed rd_options
457 : : *
458 : : * tuple is the real pg_class tuple (not rd_rel!) for relation
459 : : *
460 : : * Note: rd_rel and (if an index) rd_indam must be valid already
461 : : */
462 : : static void
6495 tgl@sss.pgh.pa.us 463 : 711248 : RelationParseRelOptions(Relation relation, HeapTuple tuple)
464 : : {
465 : : bytea *options;
466 : : amoptions_function amoptsfn;
467 : :
468 : 711248 : relation->rd_options = NULL;
469 : :
470 : : /*
471 : : * Look up any AM-specific parse function; fall out if relkind should not
472 : : * have options.
473 : : */
6496 bruce@momjian.us 474 [ + + + ]: 711248 : switch (relation->rd_rel->relkind)
475 : : {
6495 tgl@sss.pgh.pa.us 476 : 384126 : case RELKIND_RELATION:
477 : : case RELKIND_TOASTVALUE:
478 : : case RELKIND_VIEW:
479 : : case RELKIND_MATVIEW:
480 : : case RELKIND_PARTITIONED_TABLE:
2277 alvherre@alvh.no-ip. 481 : 384126 : amoptsfn = NULL;
482 : 384126 : break;
483 : 322329 : case RELKIND_INDEX:
484 : : case RELKIND_PARTITIONED_INDEX:
1910 andres@anarazel.de 485 : 322329 : amoptsfn = relation->rd_indam->amoptions;
6495 tgl@sss.pgh.pa.us 486 : 322329 : break;
487 : 4793 : default:
488 : 4793 : return;
489 : : }
490 : :
491 : : /*
492 : : * Fetch reloptions from tuple; have to use a hardwired descriptor because
493 : : * we might not have any other for pg_class yet (consider executing this
494 : : * code for pg_class itself)
495 : : */
3 akorotkov@postgresql 496 : 706455 : options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn);
497 : :
498 : : /*
499 : : * Copy parsed data into CacheMemoryContext. To guard against the
500 : : * possibility of leaks in the reloptions code, we want to do the actual
501 : : * parsing in the caller's memory context and copy the results into
502 : : * CacheMemoryContext after the fact.
503 : : */
6495 tgl@sss.pgh.pa.us 504 [ + + ]: 706455 : if (options)
505 : : {
506 : 13314 : relation->rd_options = MemoryContextAlloc(CacheMemoryContext,
507 : 6657 : VARSIZE(options));
508 : 6657 : memcpy(relation->rd_options, options, VARSIZE(options));
5206 509 : 6657 : pfree(options);
510 : : }
511 : : }
512 : :
513 : : /*
514 : : * RelationBuildTupleDesc
515 : : *
516 : : * Form the relation's tuple descriptor from information in
517 : : * the pg_attribute, pg_attrdef & pg_constraint system catalogs.
518 : : */
519 : : static void
6940 520 : 665227 : RelationBuildTupleDesc(Relation relation)
521 : : {
522 : : HeapTuple pg_attribute_tuple;
523 : : Relation pg_attribute_desc;
524 : : SysScanDesc pg_attribute_scan;
525 : : ScanKeyData skey[2];
526 : : int need;
527 : : TupleConstr *constr;
2209 andrew@dunslane.net 528 : 665227 : AttrMissing *attrmiss = NULL;
8768 bruce@momjian.us 529 : 665227 : int ndef = 0;
530 : :
531 : : /* fill rd_att's type ID fields (compare heap.c's AddNewRelationTuple) */
1377 tgl@sss.pgh.pa.us 532 : 665227 : relation->rd_att->tdtypeid =
533 [ + + ]: 665227 : relation->rd_rel->reltype ? relation->rd_rel->reltype : RECORDOID;
534 : 665227 : relation->rd_att->tdtypmod = -1; /* just to be sure */
535 : :
1104 536 : 665227 : constr = (TupleConstr *) MemoryContextAllocZero(CacheMemoryContext,
537 : : sizeof(TupleConstr));
8822 inoue@tpf.co.jp 538 : 665227 : constr->has_not_null = false;
1842 peter@eisentraut.org 539 : 665227 : constr->has_generated_stored = false;
540 : :
541 : : /*
542 : : * Form a scan key that selects only user attributes (attnum > 0).
543 : : * (Eliminating system attribute rows at the index level is lots faster
544 : : * than fetching them.)
545 : : */
7459 tgl@sss.pgh.pa.us 546 : 665227 : ScanKeyInit(&skey[0],
547 : : Anum_pg_attribute_attrelid,
548 : : BTEqualStrategyNumber, F_OIDEQ,
549 : : ObjectIdGetDatum(RelationGetRelid(relation)));
550 : 665227 : ScanKeyInit(&skey[1],
551 : : Anum_pg_attribute_attnum,
552 : : BTGreaterStrategyNumber, F_INT2GT,
553 : : Int16GetDatum(0));
554 : :
555 : : /*
556 : : * Open pg_attribute and begin a scan. Force heap scan if we haven't yet
557 : : * built the critical relcache entries (this includes initdb and startup
558 : : * without a pg_internal.init file).
559 : : */
1910 andres@anarazel.de 560 : 665227 : pg_attribute_desc = table_open(AttributeRelationId, AccessShareLock);
8090 tgl@sss.pgh.pa.us 561 : 665227 : pg_attribute_scan = systable_beginscan(pg_attribute_desc,
562 : : AttributeRelidNumIndexId,
563 : : criticalRelcachesBuilt,
564 : : NULL,
565 : : 2, skey);
566 : :
567 : : /*
568 : : * add attribute data to relation->rd_att
569 : : */
2199 teodor@sigaev.ru 570 : 665227 : need = RelationGetNumberOfAttributes(relation);
571 : :
8090 tgl@sss.pgh.pa.us 572 [ + + ]: 2243234 : while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan)))
573 : : {
574 : : Form_pg_attribute attp;
575 : : int attnum;
576 : :
9357 bruce@momjian.us 577 : 2242719 : attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
578 : :
2209 andrew@dunslane.net 579 : 2242719 : attnum = attp->attnum;
2199 teodor@sigaev.ru 580 [ + - - + ]: 2242719 : if (attnum <= 0 || attnum > RelationGetNumberOfAttributes(relation))
1104 tgl@sss.pgh.pa.us 581 [ # # ]:UBC 0 : elog(ERROR, "invalid attribute number %d for relation \"%s\"",
582 : : attp->attnum, RelationGetRelationName(relation));
583 : :
2209 andrew@dunslane.net 584 :CBC 2242719 : memcpy(TupleDescAttr(relation->rd_att, attnum - 1),
585 : : attp,
586 : : ATTRIBUTE_FIXED_PART_SIZE);
587 : :
588 : : /* Update constraint/default info */
8055 tgl@sss.pgh.pa.us 589 [ + + ]: 2242719 : if (attp->attnotnull)
8090 590 : 886881 : constr->has_not_null = true;
1842 peter@eisentraut.org 591 [ + + ]: 2242719 : if (attp->attgenerated == ATTRIBUTE_GENERATED_STORED)
592 : 4203 : constr->has_generated_stored = true;
8090 tgl@sss.pgh.pa.us 593 [ + + ]: 2242719 : if (attp->atthasdef)
594 : 19169 : ndef++;
595 : :
596 : : /* If the column has a "missing" value, put it in the attrmiss array */
2209 andrew@dunslane.net 597 [ + + ]: 2242719 : if (attp->atthasmissing)
598 : : {
599 : : Datum missingval;
600 : : bool missingNull;
601 : :
602 : : /* Do we have a missing value? */
603 : 3540 : missingval = heap_getattr(pg_attribute_tuple,
604 : : Anum_pg_attribute_attmissingval,
605 : : pg_attribute_desc->rd_att,
606 : : &missingNull);
607 [ + - ]: 3540 : if (!missingNull)
608 : : {
609 : : /* Yes, fetch from the array */
610 : : MemoryContext oldcxt;
611 : : bool is_null;
612 : 3540 : int one = 1;
613 : : Datum missval;
614 : :
615 [ + + ]: 3540 : if (attrmiss == NULL)
616 : : attrmiss = (AttrMissing *)
617 : 1638 : MemoryContextAllocZero(CacheMemoryContext,
618 : 1638 : relation->rd_rel->relnatts *
619 : : sizeof(AttrMissing));
620 : :
621 : 3540 : missval = array_get_element(missingval,
622 : : 1,
623 : : &one,
624 : : -1,
625 : 3540 : attp->attlen,
626 : 3540 : attp->attbyval,
627 : 3540 : attp->attalign,
628 : : &is_null);
629 [ - + ]: 3540 : Assert(!is_null);
630 [ + + ]: 3540 : if (attp->attbyval)
631 : : {
632 : : /* for copy by val just copy the datum direct */
2118 akapila@postgresql.o 633 : 2190 : attrmiss[attnum - 1].am_value = missval;
634 : : }
635 : : else
636 : : {
637 : : /* otherwise copy in the correct context */
2209 andrew@dunslane.net 638 : 1350 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
2118 akapila@postgresql.o 639 : 2700 : attrmiss[attnum - 1].am_value = datumCopy(missval,
640 : 1350 : attp->attbyval,
641 : 1350 : attp->attlen);
2209 andrew@dunslane.net 642 : 1350 : MemoryContextSwitchTo(oldcxt);
643 : : }
2118 akapila@postgresql.o 644 : 3540 : attrmiss[attnum - 1].am_present = true;
645 : : }
646 : : }
8090 tgl@sss.pgh.pa.us 647 : 2242719 : need--;
648 [ + + ]: 2242719 : if (need == 0)
649 : 664712 : break;
650 : : }
651 : :
652 : : /*
653 : : * end the scan and close the attribute relation
654 : : */
655 : 665227 : systable_endscan(pg_attribute_scan);
1910 andres@anarazel.de 656 : 665227 : table_close(pg_attribute_desc, AccessShareLock);
657 : :
8090 tgl@sss.pgh.pa.us 658 [ - + ]: 665227 : if (need != 0)
1104 tgl@sss.pgh.pa.us 659 [ # # ]:UBC 0 : elog(ERROR, "pg_attribute catalog is missing %d attribute(s) for relation OID %u",
660 : : need, RelationGetRelid(relation));
661 : :
662 : : /*
663 : : * The attcacheoff values we read from pg_attribute should all be -1
664 : : * ("unknown"). Verify this if assert checking is on. They will be
665 : : * computed when and if needed during tuple access.
666 : : */
667 : : #ifdef USE_ASSERT_CHECKING
668 : : {
669 : : int i;
670 : :
2199 teodor@sigaev.ru 671 [ + + ]:CBC 2907946 : for (i = 0; i < RelationGetNumberOfAttributes(relation); i++)
2429 andres@anarazel.de 672 [ - + ]: 2242719 : Assert(TupleDescAttr(relation->rd_att, i)->attcacheoff == -1);
673 : : }
674 : : #endif
675 : :
676 : : /*
677 : : * However, we can easily set the attcacheoff value for the first
678 : : * attribute: it must be zero. This eliminates the need for special cases
679 : : * for attnum=1 that used to exist in fastgetattr() and index_getattr().
680 : : */
2199 teodor@sigaev.ru 681 [ + + ]: 665227 : if (RelationGetNumberOfAttributes(relation) > 0)
2429 andres@anarazel.de 682 : 664712 : TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0;
683 : :
684 : : /*
685 : : * Set up constraint/default info
686 : : */
1529 peter@eisentraut.org 687 [ + + ]: 665227 : if (constr->has_not_null ||
688 [ + + + + ]: 478385 : constr->has_generated_stored ||
689 [ + + ]: 474275 : ndef > 0 ||
690 : 474263 : attrmiss ||
1104 tgl@sss.pgh.pa.us 691 [ + + ]: 474263 : relation->rd_rel->relchecks > 0)
692 : : {
8090 693 : 193557 : relation->rd_att->constr = constr;
694 : :
695 [ + + ]: 193557 : if (ndef > 0) /* DEFAULTs */
1104 696 : 13607 : AttrDefaultFetch(relation, ndef);
697 : : else
8090 698 : 179950 : constr->num_defval = 0;
699 : :
2209 andrew@dunslane.net 700 : 193557 : constr->missing = attrmiss;
701 : :
8090 tgl@sss.pgh.pa.us 702 [ + + ]: 193557 : if (relation->rd_rel->relchecks > 0) /* CHECKs */
7947 703 : 5513 : CheckConstraintFetch(relation);
704 : : else
8090 705 : 188044 : constr->num_check = 0;
706 : : }
707 : : else
708 : : {
709 : 471670 : pfree(constr);
710 : 471670 : relation->rd_att->constr = NULL;
711 : : }
10141 scrappy@hub.org 712 : 665227 : }
713 : :
714 : : /*
715 : : * RelationBuildRuleLock
716 : : *
717 : : * Form the relation's rewrite rules from information in
718 : : * the pg_rewrite system catalog.
719 : : *
720 : : * Note: The rule parsetrees are potentially very complex node structures.
721 : : * To allow these trees to be freed when the relcache entry is flushed,
722 : : * we make a private memory context to hold the RuleLock information for
723 : : * each relcache entry that has associated rules. The context is used
724 : : * just for rule info, not for any other subsidiary data of the relcache
725 : : * entry, because that keeps the update logic in RelationClearRelation()
726 : : * manageable. The other subsidiary data structures are simple enough
727 : : * to be easy to free explicitly, anyway.
728 : : *
729 : : * Note: The relation's reloptions must have been extracted first.
730 : : */
731 : : static void
732 : 16310 : RelationBuildRuleLock(Relation relation)
733 : : {
734 : : MemoryContext rulescxt;
735 : : MemoryContext oldcxt;
736 : : HeapTuple rewrite_tuple;
737 : : Relation rewrite_desc;
738 : : TupleDesc rewrite_tupdesc;
739 : : SysScanDesc rewrite_scan;
740 : : ScanKeyData key;
741 : : RuleLock *rulelock;
742 : : int numlocks;
743 : : RewriteRule **rules;
744 : : int maxlocks;
745 : :
746 : : /*
747 : : * Make the private context. Assume it'll not contain much data.
748 : : */
2210 tgl@sss.pgh.pa.us 749 : 16310 : rulescxt = AllocSetContextCreate(CacheMemoryContext,
750 : : "relation rules",
751 : : ALLOCSET_SMALL_SIZES);
8689 752 : 16310 : relation->rd_rulescxt = rulescxt;
2200 peter_e@gmx.net 753 : 16310 : MemoryContextCopyAndSetIdentifier(rulescxt,
754 : : RelationGetRelationName(relation));
755 : :
756 : : /*
757 : : * allocate an array to hold the rewrite rules (the array is extended if
758 : : * necessary)
759 : : */
9716 bruce@momjian.us 760 : 16310 : maxlocks = 4;
761 : : rules = (RewriteRule **)
8689 tgl@sss.pgh.pa.us 762 : 16310 : MemoryContextAlloc(rulescxt, sizeof(RewriteRule *) * maxlocks);
9716 bruce@momjian.us 763 : 16310 : numlocks = 0;
764 : :
765 : : /*
766 : : * form a scan key
767 : : */
7459 tgl@sss.pgh.pa.us 768 : 16310 : ScanKeyInit(&key,
769 : : Anum_pg_rewrite_ev_class,
770 : : BTEqualStrategyNumber, F_OIDEQ,
771 : : ObjectIdGetDatum(RelationGetRelid(relation)));
772 : :
773 : : /*
774 : : * open pg_rewrite and begin a scan
775 : : *
776 : : * Note: since we scan the rules using RewriteRelRulenameIndexId, we will
777 : : * be reading the rules in name order, except possibly during
778 : : * emergency-recovery operations (ie, IgnoreSystemIndexes). This in turn
779 : : * ensures that rules will be fired in name order.
780 : : */
1910 andres@anarazel.de 781 : 16310 : rewrite_desc = table_open(RewriteRelationId, AccessShareLock);
8031 tgl@sss.pgh.pa.us 782 : 16310 : rewrite_tupdesc = RelationGetDescr(rewrite_desc);
7893 bruce@momjian.us 783 : 16310 : rewrite_scan = systable_beginscan(rewrite_desc,
784 : : RewriteRelRulenameIndexId,
785 : : true, NULL,
786 : : 1, &key);
787 : :
8031 tgl@sss.pgh.pa.us 788 [ + + ]: 31987 : while (HeapTupleIsValid(rewrite_tuple = systable_getnext(rewrite_scan)))
789 : : {
790 : 15677 : Form_pg_rewrite rewrite_form = (Form_pg_rewrite) GETSTRUCT(rewrite_tuple);
791 : : bool isnull;
792 : : Datum rule_datum;
793 : : char *rule_str;
794 : : RewriteRule *rule;
795 : : Oid check_as_user;
796 : :
8689 797 : 15677 : rule = (RewriteRule *) MemoryContextAlloc(rulescxt,
798 : : sizeof(RewriteRule));
799 : :
1972 andres@anarazel.de 800 : 15677 : rule->ruleId = rewrite_form->oid;
801 : :
8032 tgl@sss.pgh.pa.us 802 : 15677 : rule->event = rewrite_form->ev_type - '0';
6236 JanWieck@Yahoo.com 803 : 15677 : rule->enabled = rewrite_form->ev_enabled;
8032 tgl@sss.pgh.pa.us 804 : 15677 : rule->isInstead = rewrite_form->is_instead;
805 : :
806 : : /*
807 : : * Must use heap_getattr to fetch ev_action and ev_qual. Also, the
808 : : * rule strings are often large enough to be toasted. To avoid
809 : : * leaking memory in the caller's context, do the detoasting here so
810 : : * we can free the detoasted version.
811 : : */
6671 812 : 15677 : rule_datum = heap_getattr(rewrite_tuple,
813 : : Anum_pg_rewrite_ev_action,
814 : : rewrite_tupdesc,
815 : : &isnull);
8424 bruce@momjian.us 816 [ - + ]: 15677 : Assert(!isnull);
5864 tgl@sss.pgh.pa.us 817 : 15677 : rule_str = TextDatumGetCString(rule_datum);
8499 818 : 15677 : oldcxt = MemoryContextSwitchTo(rulescxt);
6671 819 : 15677 : rule->actions = (List *) stringToNode(rule_str);
8689 820 : 15677 : MemoryContextSwitchTo(oldcxt);
6671 821 : 15677 : pfree(rule_str);
822 : :
823 : 15677 : rule_datum = heap_getattr(rewrite_tuple,
824 : : Anum_pg_rewrite_ev_qual,
825 : : rewrite_tupdesc,
826 : : &isnull);
8424 bruce@momjian.us 827 [ - + ]: 15677 : Assert(!isnull);
5864 tgl@sss.pgh.pa.us 828 : 15677 : rule_str = TextDatumGetCString(rule_datum);
8499 829 : 15677 : oldcxt = MemoryContextSwitchTo(rulescxt);
6671 830 : 15677 : rule->qual = (Node *) stringToNode(rule_str);
8689 831 : 15677 : MemoryContextSwitchTo(oldcxt);
6671 832 : 15677 : pfree(rule_str);
833 : :
834 : : /*
835 : : * If this is a SELECT rule defining a view, and the view has
836 : : * "security_invoker" set, we must perform all permissions checks on
837 : : * relations referred to by the rule as the invoking user.
838 : : *
839 : : * In all other cases (including non-SELECT rules on security invoker
840 : : * views), perform the permissions checks as the relation owner.
841 : : */
754 dean.a.rasheed@gmail 842 [ + + ]: 15677 : if (rule->event == CMD_SELECT &&
843 [ + + + + ]: 27277 : relation->rd_rel->relkind == RELKIND_VIEW &&
844 [ - + + + : 12800 : RelationHasSecurityInvoker(relation))
+ + ]
845 : 78 : check_as_user = InvalidOid;
846 : : else
847 : 15599 : check_as_user = relation->rd_rel->relowner;
848 : :
849 : : /*
850 : : * Scan through the rule's actions and set the checkAsUser field on
851 : : * all RTEPermissionInfos. We have to look at the qual as well, in
852 : : * case it contains sublinks.
853 : : *
854 : : * The reason for doing this when the rule is loaded, rather than when
855 : : * it is stored, is that otherwise ALTER TABLE OWNER would have to
856 : : * grovel through stored rules to update checkAsUser fields. Scanning
857 : : * the rule tree during load is relatively cheap (compared to
858 : : * constructing it in the first place), so we do it here.
859 : : */
860 : 15677 : setRuleCheckAsUser((Node *) rule->actions, check_as_user);
861 : 15677 : setRuleCheckAsUser(rule->qual, check_as_user);
862 : :
8840 tgl@sss.pgh.pa.us 863 [ + + ]: 15677 : if (numlocks >= maxlocks)
864 : : {
9716 bruce@momjian.us 865 : 17 : maxlocks *= 2;
866 : : rules = (RewriteRule **)
8689 tgl@sss.pgh.pa.us 867 : 17 : repalloc(rules, sizeof(RewriteRule *) * maxlocks);
868 : : }
8840 869 : 15677 : rules[numlocks++] = rule;
870 : : }
871 : :
872 : : /*
873 : : * end the scan and close the attribute relation
874 : : */
8031 875 : 16310 : systable_endscan(rewrite_scan);
1910 andres@anarazel.de 876 : 16310 : table_close(rewrite_desc, AccessShareLock);
877 : :
878 : : /*
879 : : * there might not be any rules (if relhasrules is out-of-date)
880 : : */
5634 tgl@sss.pgh.pa.us 881 [ + + ]: 16310 : if (numlocks == 0)
882 : : {
883 : 1367 : relation->rd_rules = NULL;
884 : 1367 : relation->rd_rulescxt = NULL;
885 : 1367 : MemoryContextDelete(rulescxt);
886 : 1367 : return;
887 : : }
888 : :
889 : : /*
890 : : * form a RuleLock and insert into relation
891 : : */
8689 892 : 14943 : rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));
9716 bruce@momjian.us 893 : 14943 : rulelock->numLocks = numlocks;
894 : 14943 : rulelock->rules = rules;
895 : :
896 : 14943 : relation->rd_rules = rulelock;
897 : : }
898 : :
899 : : /*
900 : : * equalRuleLocks
901 : : *
902 : : * Determine whether two RuleLocks are equivalent
903 : : *
904 : : * Probably this should be in the rules code someplace...
905 : : */
906 : : static bool
8840 tgl@sss.pgh.pa.us 907 : 141583 : equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
908 : : {
909 : : int i;
910 : :
911 : : /*
912 : : * As of 7.3 we assume the rule ordering is repeatable, because
913 : : * RelationBuildRuleLock should read 'em in a consistent order. So just
914 : : * compare corresponding slots.
915 : : */
916 [ + + ]: 141583 : if (rlock1 != NULL)
917 : : {
918 [ + + ]: 989 : if (rlock2 == NULL)
919 : 22 : return false;
920 [ + + ]: 967 : if (rlock1->numLocks != rlock2->numLocks)
921 : 20 : return false;
922 [ + + ]: 1769 : for (i = 0; i < rlock1->numLocks; i++)
923 : : {
924 : 965 : RewriteRule *rule1 = rlock1->rules[i];
8031 925 : 965 : RewriteRule *rule2 = rlock2->rules[i];
926 : :
927 [ - + ]: 965 : if (rule1->ruleId != rule2->ruleId)
8840 tgl@sss.pgh.pa.us 928 :UBC 0 : return false;
8840 tgl@sss.pgh.pa.us 929 [ - + ]:CBC 965 : if (rule1->event != rule2->event)
8840 tgl@sss.pgh.pa.us 930 :UBC 0 : return false;
5584 tgl@sss.pgh.pa.us 931 [ + + ]:CBC 965 : if (rule1->enabled != rule2->enabled)
932 : 23 : return false;
8840 933 [ - + ]: 942 : if (rule1->isInstead != rule2->isInstead)
8840 tgl@sss.pgh.pa.us 934 :UBC 0 : return false;
8768 bruce@momjian.us 935 [ - + ]:CBC 942 : if (!equal(rule1->qual, rule2->qual))
8840 tgl@sss.pgh.pa.us 936 :UBC 0 : return false;
8768 bruce@momjian.us 937 [ + + ]:CBC 942 : if (!equal(rule1->actions, rule2->actions))
8840 tgl@sss.pgh.pa.us 938 : 120 : return false;
939 : : }
940 : : }
941 [ + + ]: 140594 : else if (rlock2 != NULL)
942 : 6763 : return false;
943 : 134635 : return true;
944 : : }
945 : :
946 : : /*
947 : : * equalPolicy
948 : : *
949 : : * Determine whether two policies are equivalent
950 : : */
951 : : static bool
3490 sfrost@snowman.net 952 : 93 : equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
953 : : {
954 : : int i;
955 : : Oid *r1,
956 : : *r2;
957 : :
958 [ + - ]: 93 : if (policy1 != NULL)
959 : : {
960 [ - + ]: 93 : if (policy2 == NULL)
3490 sfrost@snowman.net 961 :UBC 0 : return false;
962 : :
3368 tgl@sss.pgh.pa.us 963 [ - + ]:CBC 93 : if (policy1->polcmd != policy2->polcmd)
3490 sfrost@snowman.net 964 :UBC 0 : return false;
3488 sfrost@snowman.net 965 [ - + ]:CBC 93 : if (policy1->hassublinks != policy2->hassublinks)
3490 sfrost@snowman.net 966 :UBC 0 : return false;
3249 bruce@momjian.us 967 [ - + ]:CBC 93 : if (strcmp(policy1->policy_name, policy2->policy_name) != 0)
3490 sfrost@snowman.net 968 :UBC 0 : return false;
3490 sfrost@snowman.net 969 [ - + ]:CBC 93 : if (ARR_DIMS(policy1->roles)[0] != ARR_DIMS(policy2->roles)[0])
3490 sfrost@snowman.net 970 :UBC 0 : return false;
971 : :
3490 sfrost@snowman.net 972 [ - + ]:CBC 93 : r1 = (Oid *) ARR_DATA_PTR(policy1->roles);
973 [ - + ]: 93 : r2 = (Oid *) ARR_DATA_PTR(policy2->roles);
974 : :
975 [ + + ]: 186 : for (i = 0; i < ARR_DIMS(policy1->roles)[0]; i++)
976 : : {
977 [ - + ]: 93 : if (r1[i] != r2[i])
3490 sfrost@snowman.net 978 :UBC 0 : return false;
979 : : }
980 : :
3285 sfrost@snowman.net 981 [ - + ]:CBC 93 : if (!equal(policy1->qual, policy2->qual))
3490 sfrost@snowman.net 982 :UBC 0 : return false;
3490 sfrost@snowman.net 983 [ - + ]:CBC 93 : if (!equal(policy1->with_check_qual, policy2->with_check_qual))
3490 sfrost@snowman.net 984 :UBC 0 : return false;
985 : : }
986 [ # # ]: 0 : else if (policy2 != NULL)
987 : 0 : return false;
988 : :
3490 sfrost@snowman.net 989 :CBC 93 : return true;
990 : : }
991 : :
992 : : /*
993 : : * equalRSDesc
994 : : *
995 : : * Determine whether two RowSecurityDesc's are equivalent
996 : : */
997 : : static bool
998 : 141583 : equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
999 : : {
1000 : : ListCell *lc,
1001 : : *rc;
1002 : :
1003 [ + + + + ]: 141583 : if (rsdesc1 == NULL && rsdesc2 == NULL)
1004 : 141369 : return true;
1005 : :
1006 [ + + + + : 214 : if ((rsdesc1 != NULL && rsdesc2 == NULL) ||
+ + ]
1007 [ + - ]: 139 : (rsdesc1 == NULL && rsdesc2 != NULL))
1008 : 144 : return false;
1009 : :
1010 [ - + ]: 70 : if (list_length(rsdesc1->policies) != list_length(rsdesc2->policies))
3490 sfrost@snowman.net 1011 :UBC 0 : return false;
1012 : :
1013 : : /* RelationBuildRowSecurity should build policies in order */
3490 sfrost@snowman.net 1014 [ + + + + :CBC 163 : forboth(lc, rsdesc1->policies, rc, rsdesc2->policies)
+ + + + +
+ + - +
+ ]
1015 : : {
3249 bruce@momjian.us 1016 : 93 : RowSecurityPolicy *l = (RowSecurityPolicy *) lfirst(lc);
1017 : 93 : RowSecurityPolicy *r = (RowSecurityPolicy *) lfirst(rc);
1018 : :
1019 [ - + ]: 93 : if (!equalPolicy(l, r))
3490 sfrost@snowman.net 1020 :UBC 0 : return false;
1021 : : }
1022 : :
3488 sfrost@snowman.net 1023 :CBC 70 : return true;
1024 : : }
1025 : :
1026 : : /*
1027 : : * RelationBuildDesc
1028 : : *
1029 : : * Build a relation descriptor. The caller must hold at least
1030 : : * AccessShareLock on the target relid.
1031 : : *
1032 : : * The new descriptor is inserted into the hash table if insertIt is true.
1033 : : *
1034 : : * Returns NULL if no pg_class row could be found for the given relid
1035 : : * (suggesting we are trying to access a just-deleted relation).
1036 : : * Any other error is reported via elog.
1037 : : */
1038 : : static Relation
5206 tgl@sss.pgh.pa.us 1039 : 665881 : RelationBuildDesc(Oid targetRelId, bool insertIt)
1040 : : {
1041 : : int in_progress_offset;
1042 : : Relation relation;
1043 : : Oid relid;
1044 : : HeapTuple pg_class_tuple;
1045 : : Form_pg_class relp;
1046 : :
1047 : : /*
1048 : : * This function and its subroutines can allocate a good deal of transient
1049 : : * data in CurrentMemoryContext. Traditionally we've just leaked that
1050 : : * data, reasoning that the caller's context is at worst of transaction
1051 : : * scope, and relcache loads shouldn't happen so often that it's essential
1052 : : * to recover transient data before end of statement/transaction. However
1053 : : * that's definitely not true when debug_discard_caches is active, and
1054 : : * perhaps it's not true in other cases.
1055 : : *
1056 : : * When debug_discard_caches is active or when forced to by
1057 : : * RECOVER_RELATION_BUILD_MEMORY=1, arrange to allocate the junk in a
1058 : : * temporary context that we'll free before returning. Make it a child of
1059 : : * caller's context so that it will get cleaned up appropriately if we
1060 : : * error out partway through.
1061 : : */
1062 : : #ifdef MAYBE_RECOVER_RELATION_BUILD_MEMORY
1194 peter@eisentraut.org 1063 : 665881 : MemoryContext tmpcxt = NULL;
1064 : 665881 : MemoryContext oldcxt = NULL;
1065 : :
1006 tgl@sss.pgh.pa.us 1066 [ - + ]: 665881 : if (RECOVER_RELATION_BUILD_MEMORY || debug_discard_caches > 0)
1067 : : {
1194 peter@eisentraut.org 1068 :UBC 0 : tmpcxt = AllocSetContextCreate(CurrentMemoryContext,
1069 : : "RelationBuildDesc workspace",
1070 : : ALLOCSET_DEFAULT_SIZES);
1071 : 0 : oldcxt = MemoryContextSwitchTo(tmpcxt);
1072 : : }
1073 : : #endif
1074 : :
1075 : : /* Register to catch invalidation messages */
904 noah@leadboat.com 1076 [ + + ]:CBC 665881 : if (in_progress_list_len >= in_progress_list_maxlen)
1077 : : {
1078 : : int allocsize;
1079 : :
904 noah@leadboat.com 1080 :GBC 1 : allocsize = in_progress_list_maxlen * 2;
1081 : 1 : in_progress_list = repalloc(in_progress_list,
1082 : : allocsize * sizeof(*in_progress_list));
1083 : 1 : in_progress_list_maxlen = allocsize;
1084 : : }
904 noah@leadboat.com 1085 :CBC 665881 : in_progress_offset = in_progress_list_len++;
1086 : 665881 : in_progress_list[in_progress_offset].reloid = targetRelId;
1087 : 665902 : retry:
1088 : 665902 : in_progress_list[in_progress_offset].invalidated = false;
1089 : :
1090 : : /*
1091 : : * find the tuple in pg_class corresponding to the given relation id
1092 : : */
3695 rhaas@postgresql.org 1093 : 665902 : pg_class_tuple = ScanPgRelation(targetRelId, true, false);
1094 : :
1095 : : /*
1096 : : * if no such tuple exists, return NULL
1097 : : */
9716 bruce@momjian.us 1098 [ + + ]: 665898 : if (!HeapTupleIsValid(pg_class_tuple))
1099 : : {
1100 : : #ifdef MAYBE_RECOVER_RELATION_BUILD_MEMORY
1194 peter@eisentraut.org 1101 [ - + ]: 671 : if (tmpcxt)
1102 : : {
1103 : : /* Return to caller's context, and blow away the temporary context */
1194 peter@eisentraut.org 1104 :UBC 0 : MemoryContextSwitchTo(oldcxt);
1105 : 0 : MemoryContextDelete(tmpcxt);
1106 : : }
1107 : : #endif
904 noah@leadboat.com 1108 [ - + ]:CBC 671 : Assert(in_progress_offset + 1 == in_progress_list_len);
1109 : 671 : in_progress_list_len--;
9716 bruce@momjian.us 1110 : 671 : return NULL;
1111 : : }
1112 : :
1113 : : /*
1114 : : * get information from the pg_class_tuple
1115 : : */
1116 : 665227 : relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
1972 andres@anarazel.de 1117 : 665227 : relid = relp->oid;
5180 tgl@sss.pgh.pa.us 1118 [ - + ]: 665227 : Assert(relid == targetRelId);
1119 : :
1120 : : /*
1121 : : * allocate storage for the relation descriptor, and copy pg_class_tuple
1122 : : * to relation->rd_rel.
1123 : : */
5206 1124 : 665227 : relation = AllocateRelationDesc(relp);
1125 : :
1126 : : /*
1127 : : * initialize the relation's relation id (relation->rd_id)
1128 : : */
9370 bruce@momjian.us 1129 : 665227 : RelationGetRelid(relation) = relid;
1130 : :
1131 : : /*
1132 : : * Normal relations are not nailed into the cache. Since we don't flush
1133 : : * new relations, it won't be new. It could be temp though.
1134 : : */
7211 tgl@sss.pgh.pa.us 1135 : 665227 : relation->rd_refcnt = 0;
7169 1136 : 665227 : relation->rd_isnailed = false;
7150 1137 : 665227 : relation->rd_createSubid = InvalidSubTransactionId;
648 rhaas@postgresql.org 1138 : 665227 : relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
1139 : 665227 : relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1471 noah@leadboat.com 1140 : 665227 : relation->rd_droppedSubid = InvalidSubTransactionId;
4871 rhaas@postgresql.org 1141 [ + + - ]: 665227 : switch (relation->rd_rel->relpersistence)
1142 : : {
4855 1143 : 653024 : case RELPERSISTENCE_UNLOGGED:
1144 : : case RELPERSISTENCE_PERMANENT:
42 heikki.linnakangas@i 1145 :GNC 653024 : relation->rd_backend = INVALID_PROC_NUMBER;
4136 tgl@sss.pgh.pa.us 1146 :CBC 653024 : relation->rd_islocaltemp = false;
4871 rhaas@postgresql.org 1147 : 653024 : break;
1148 : 12203 : case RELPERSISTENCE_TEMP:
3520 bruce@momjian.us 1149 [ + + ]: 12203 : if (isTempOrTempToastNamespace(relation->rd_rel->relnamespace))
1150 : : {
42 heikki.linnakangas@i 1151 [ + - ]:GNC 12186 : relation->rd_backend = ProcNumberForTempRelations();
4136 tgl@sss.pgh.pa.us 1152 :CBC 12186 : relation->rd_islocaltemp = true;
1153 : : }
1154 : : else
1155 : : {
1156 : : /*
1157 : : * If it's a temp table, but not one of ours, we have to use
1158 : : * the slow, grotty method to figure out the owning backend.
1159 : : *
1160 : : * Note: it's possible that rd_backend gets set to
1161 : : * MyProcNumber here, in case we are looking at a pg_class
1162 : : * entry left over from a crashed backend that coincidentally
1163 : : * had the same ProcNumber we're using. We should *not*
1164 : : * consider such a table to be "ours"; this is why we need the
1165 : : * separate rd_islocaltemp flag. The pg_class entry will get
1166 : : * flushed if/when we clean out the corresponding temp table
1167 : : * namespace in preparation for using it.
1168 : : */
4871 rhaas@postgresql.org 1169 : 17 : relation->rd_backend =
42 heikki.linnakangas@i 1170 :GNC 17 : GetTempNamespaceProcNumber(relation->rd_rel->relnamespace);
1171 [ - + ]: 17 : Assert(relation->rd_backend != INVALID_PROC_NUMBER);
4136 tgl@sss.pgh.pa.us 1172 :CBC 17 : relation->rd_islocaltemp = false;
1173 : : }
4871 rhaas@postgresql.org 1174 : 12203 : break;
4871 rhaas@postgresql.org 1175 :UBC 0 : default:
1176 [ # # ]: 0 : elog(ERROR, "invalid relpersistence: %c",
1177 : : relation->rd_rel->relpersistence);
1178 : : break;
1179 : : }
1180 : :
1181 : : /*
1182 : : * initialize the tuple descriptor (relation->rd_att).
1183 : : */
6940 tgl@sss.pgh.pa.us 1184 :CBC 665227 : RelationBuildTupleDesc(relation);
1185 : :
1186 : : /* foreign key data is not loaded till asked for */
2857 1187 : 665227 : relation->rd_fkeylist = NIL;
1188 : 665227 : relation->rd_fkeyvalid = false;
1189 : :
1190 : : /* partitioning data is not loaded till asked for */
1572 1191 : 665227 : relation->rd_partkey = NULL;
1192 : 665227 : relation->rd_partkeycxt = NULL;
1193 : 665227 : relation->rd_partdesc = NULL;
1082 alvherre@alvh.no-ip. 1194 : 665227 : relation->rd_partdesc_nodetached = NULL;
1195 : 665227 : relation->rd_partdesc_nodetached_xmin = InvalidTransactionId;
1572 tgl@sss.pgh.pa.us 1196 : 665227 : relation->rd_pdcxt = NULL;
1082 alvherre@alvh.no-ip. 1197 : 665227 : relation->rd_pddcxt = NULL;
1828 tgl@sss.pgh.pa.us 1198 : 665227 : relation->rd_partcheck = NIL;
1199 : 665227 : relation->rd_partcheckvalid = false;
1200 : 665227 : relation->rd_partcheckcxt = NULL;
1201 : :
1202 : : /*
1203 : : * initialize access method information
1204 : : */
863 peter@eisentraut.org 1205 [ + + ]: 665227 : if (relation->rd_rel->relkind == RELKIND_INDEX ||
1206 [ + + ]: 380637 : relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
1207 : 287654 : RelationInitIndexAccessInfo(relation);
1208 [ + + + + : 377573 : else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) ||
+ + ]
1209 [ + + ]: 52574 : relation->rd_rel->relkind == RELKIND_SEQUENCE)
1210 : 327601 : RelationInitTableAccessMethod(relation);
20 alvherre@alvh.no-ip. 1211 [ + + ]:GNC 49972 : else if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1212 : : {
1213 : : /*
1214 : : * Do nothing: access methods are a setting that partitions can
1215 : : * inherit.
1216 : : */
1217 : : }
1218 : : else
863 peter@eisentraut.org 1219 [ - + ]:CBC 22527 : Assert(relation->rd_rel->relam == InvalidOid);
1220 : :
1221 : : /* extract reloptions if any */
6495 tgl@sss.pgh.pa.us 1222 : 665221 : RelationParseRelOptions(relation, pg_class_tuple);
1223 : :
1224 : : /*
1225 : : * Fetch rules and triggers that affect this relation.
1226 : : *
1227 : : * Note that RelationBuildRuleLock() relies on this being done after
1228 : : * extracting the relation's reloptions.
1229 : : */
754 dean.a.rasheed@gmail 1230 [ + + ]: 665221 : if (relation->rd_rel->relhasrules)
1231 : 16310 : RelationBuildRuleLock(relation);
1232 : : else
1233 : : {
1234 : 648911 : relation->rd_rules = NULL;
1235 : 648911 : relation->rd_rulescxt = NULL;
1236 : : }
1237 : :
1238 [ + + ]: 665221 : if (relation->rd_rel->relhastriggers)
1239 : 28237 : RelationBuildTriggers(relation);
1240 : : else
1241 : 636984 : relation->trigdesc = NULL;
1242 : :
1243 [ + + ]: 665221 : if (relation->rd_rel->relrowsecurity)
1244 : 969 : RelationBuildRowSecurity(relation);
1245 : : else
1246 : 664252 : relation->rd_rsdesc = NULL;
1247 : :
1248 : : /*
1249 : : * initialize the relation lock manager information
1250 : : */
2489 tgl@sss.pgh.pa.us 1251 : 665221 : RelationInitLockInfo(relation); /* see lmgr.c */
1252 : :
1253 : : /*
1254 : : * initialize physical addressing information for the relation
1255 : : */
7240 1256 : 665221 : RelationInitPhysicalAddr(relation);
1257 : :
1258 : : /* make sure relation is marked as having no open file yet */
7369 1259 : 665221 : relation->rd_smgr = NULL;
1260 : :
1261 : : /*
1262 : : * now we can free the memory allocated for pg_class_tuple
1263 : : */
6496 bruce@momjian.us 1264 : 665221 : heap_freetuple(pg_class_tuple);
1265 : :
1266 : : /*
1267 : : * If an invalidation arrived mid-build, start over. Between here and the
1268 : : * end of this function, don't add code that does or reasonably could read
1269 : : * system catalogs. That range must be free from invalidation processing
1270 : : * for the !insertIt case. For the insertIt case, RelationCacheInsert()
1271 : : * will enroll this relation in ordinary relcache invalidation processing,
1272 : : */
904 noah@leadboat.com 1273 [ + + ]: 665221 : if (in_progress_list[in_progress_offset].invalidated)
1274 : : {
1275 : 21 : RelationDestroyRelation(relation, false);
1276 : 21 : goto retry;
1277 : : }
1278 [ - + ]: 665200 : Assert(in_progress_offset + 1 == in_progress_list_len);
1279 : 665200 : in_progress_list_len--;
1280 : :
1281 : : /*
1282 : : * Insert newly created relation into relcache hash table, if requested.
1283 : : *
1284 : : * There is one scenario in which we might find a hashtable entry already
1285 : : * present, even though our caller failed to find it: if the relation is a
1286 : : * system catalog or index that's used during relcache load, we might have
1287 : : * recursively created the same relcache entry during the preceding steps.
1288 : : * So allow RelationCacheInsert to delete any already-present relcache
1289 : : * entry for the same OID. The already-present entry should have refcount
1290 : : * zero (else somebody forgot to close it); in the event that it doesn't,
1291 : : * we'll elog a WARNING and leak the already-present entry.
1292 : : */
5206 tgl@sss.pgh.pa.us 1293 [ + + ]: 665200 : if (insertIt)
3619 1294 [ - + - - : 523617 : RelationCacheInsert(relation, true);
- - - - ]
1295 : :
1296 : : /* It's fully valid */
7169 1297 : 665200 : relation->rd_isvalid = true;
1298 : :
1299 : : #ifdef MAYBE_RECOVER_RELATION_BUILD_MEMORY
1194 peter@eisentraut.org 1300 [ - + ]: 665200 : if (tmpcxt)
1301 : : {
1302 : : /* Return to caller's context, and blow away the temporary context */
1194 peter@eisentraut.org 1303 :UBC 0 : MemoryContextSwitchTo(oldcxt);
1304 : 0 : MemoryContextDelete(tmpcxt);
1305 : : }
1306 : : #endif
1307 : :
9716 bruce@momjian.us 1308 :CBC 665200 : return relation;
1309 : : }
1310 : :
1311 : : /*
1312 : : * Initialize the physical addressing info (RelFileLocator) for a relcache entry
1313 : : *
1314 : : * Note: at the physical level, relations in the pg_global tablespace must
1315 : : * be treated as shared, even if relisshared isn't set. Hence we do not
1316 : : * look at relisshared here.
1317 : : */
1318 : : static void
7240 tgl@sss.pgh.pa.us 1319 : 2544033 : RelationInitPhysicalAddr(Relation relation)
1320 : : {
648 rhaas@postgresql.org 1321 : 2544033 : RelFileNumber oldnumber = relation->rd_locator.relNumber;
1322 : :
1323 : : /* these relations kinds never have storage */
1927 alvherre@alvh.no-ip. 1324 [ + + + + : 2544033 : if (!RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
+ + + + +
+ ]
1325 : 65290 : return;
1326 : :
7240 tgl@sss.pgh.pa.us 1327 [ + + ]: 2478743 : if (relation->rd_rel->reltablespace)
648 rhaas@postgresql.org 1328 : 448252 : relation->rd_locator.spcOid = relation->rd_rel->reltablespace;
1329 : : else
1330 : 2030491 : relation->rd_locator.spcOid = MyDatabaseTableSpace;
1331 [ + + ]: 2478743 : if (relation->rd_locator.spcOid == GLOBALTABLESPACE_OID)
1332 : 446830 : relation->rd_locator.dbOid = InvalidOid;
1333 : : else
1334 : 2031913 : relation->rd_locator.dbOid = MyDatabaseId;
1335 : :
5180 tgl@sss.pgh.pa.us 1336 [ + + ]: 2478743 : if (relation->rd_rel->relfilenode)
1337 : : {
1338 : : /*
1339 : : * Even if we are using a decoding snapshot that doesn't represent the
1340 : : * current state of the catalog we need to make sure the filenode
1341 : : * points to the current file since the older file will be gone (or
1342 : : * truncated). The new file will still contain older rows so lookups
1343 : : * in them will work correctly. This wouldn't work correctly if
1344 : : * rewrites were allowed to change the schema in an incompatible way,
1345 : : * but those are prevented both on catalog tables and on user tables
1346 : : * declared as additional catalog tables.
1347 : : */
3695 rhaas@postgresql.org 1348 [ + + ]: 1775279 : if (HistoricSnapshotActive()
1349 [ + - + - : 2152 : && RelationIsAccessibleInLogicalDecoding(relation)
- + - - -
- + + + +
- + - - +
+ ]
1350 [ + + ]: 1363 : && IsTransactionState())
1351 : : {
1352 : : HeapTuple phys_tuple;
1353 : : Form_pg_class physrel;
1354 : :
1355 : 1348 : phys_tuple = ScanPgRelation(RelationGetRelid(relation),
2489 tgl@sss.pgh.pa.us 1356 : 1348 : RelationGetRelid(relation) != ClassOidIndexId,
1357 : : true);
3695 rhaas@postgresql.org 1358 [ - + ]: 1348 : if (!HeapTupleIsValid(phys_tuple))
3695 rhaas@postgresql.org 1359 [ # # ]:UBC 0 : elog(ERROR, "could not find pg_class entry for %u",
1360 : : RelationGetRelid(relation));
3695 rhaas@postgresql.org 1361 :CBC 1348 : physrel = (Form_pg_class) GETSTRUCT(phys_tuple);
1362 : :
1363 : 1348 : relation->rd_rel->reltablespace = physrel->reltablespace;
1364 : 1348 : relation->rd_rel->relfilenode = physrel->relfilenode;
1365 : 1348 : heap_freetuple(phys_tuple);
1366 : : }
1367 : :
648 1368 : 1775279 : relation->rd_locator.relNumber = relation->rd_rel->relfilenode;
1369 : : }
1370 : : else
1371 : : {
1372 : : /* Consult the relation mapper */
1373 : 703464 : relation->rd_locator.relNumber =
1374 : 703464 : RelationMapOidToFilenumber(relation->rd_id,
1375 : 703464 : relation->rd_rel->relisshared);
1376 [ - + ]: 703464 : if (!RelFileNumberIsValid(relation->rd_locator.relNumber))
5180 tgl@sss.pgh.pa.us 1377 [ # # ]:UBC 0 : elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1378 : : RelationGetRelationName(relation), relation->rd_id);
1379 : : }
1380 : :
1381 : : /*
1382 : : * For RelationNeedsWAL() to answer correctly on parallel workers, restore
1383 : : * rd_firstRelfilelocatorSubid. No subtransactions start or end while in
1384 : : * parallel mode, so the specific SubTransactionId does not matter.
1385 : : */
648 rhaas@postgresql.org 1386 [ + + + + ]:CBC 2478743 : if (IsParallelWorker() && oldnumber != relation->rd_locator.relNumber)
1387 : : {
1388 [ + + ]: 19759 : if (RelFileLocatorSkippingWAL(relation->rd_locator))
1389 : 150 : relation->rd_firstRelfilelocatorSubid = TopSubTransactionId;
1390 : : else
1391 : 19609 : relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1392 : : }
1393 : : }
1394 : :
1395 : : /*
1396 : : * Fill in the IndexAmRoutine for an index relation.
1397 : : *
1398 : : * relation's rd_amhandler and rd_indexcxt must be valid already.
1399 : : */
1400 : : static void
3010 tgl@sss.pgh.pa.us 1401 : 1309343 : InitIndexAmRoutine(Relation relation)
1402 : : {
1403 : : IndexAmRoutine *cached,
1404 : : *tmp;
1405 : :
1406 : : /*
1407 : : * Call the amhandler in current, short-lived memory context, just in case
1408 : : * it leaks anything (it probably won't, but let's be paranoid).
1409 : : */
1410 : 1309343 : tmp = GetIndexAmRoutine(relation->rd_amhandler);
1411 : :
1412 : : /* OK, now transfer the data into relation's rd_indexcxt. */
1413 : 1309343 : cached = (IndexAmRoutine *) MemoryContextAlloc(relation->rd_indexcxt,
1414 : : sizeof(IndexAmRoutine));
1415 : 1309343 : memcpy(cached, tmp, sizeof(IndexAmRoutine));
1910 andres@anarazel.de 1416 : 1309343 : relation->rd_indam = cached;
1417 : :
3010 tgl@sss.pgh.pa.us 1418 : 1309343 : pfree(tmp);
1419 : 1309343 : }
1420 : :
1421 : : /*
1422 : : * Initialize index-access-method support data for an index relation
1423 : : */
1424 : : void
8226 1425 : 293894 : RelationInitIndexAccessInfo(Relation relation)
1426 : : {
1427 : : HeapTuple tuple;
1428 : : Form_pg_am aform;
1429 : : Datum indcollDatum;
1430 : : Datum indclassDatum;
1431 : : Datum indoptionDatum;
1432 : : bool isnull;
1433 : : oidvector *indcoll;
1434 : : oidvector *indclass;
1435 : : int2vector *indoption;
1436 : : MemoryContext indexcxt;
1437 : : MemoryContext oldcontext;
1438 : : int indnatts;
1439 : : int indnkeyatts;
1440 : : uint16 amsupport;
1441 : :
1442 : : /*
1443 : : * Make a copy of the pg_index entry for the index. Since pg_index
1444 : : * contains variable-length and possibly-null fields, we have to do this
1445 : : * honestly rather than just treating it as a Form_pg_index struct.
1446 : : */
5173 rhaas@postgresql.org 1447 : 293894 : tuple = SearchSysCache1(INDEXRELID,
1448 : : ObjectIdGetDatum(RelationGetRelid(relation)));
8090 tgl@sss.pgh.pa.us 1449 [ - + ]: 293894 : if (!HeapTupleIsValid(tuple))
7569 tgl@sss.pgh.pa.us 1450 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for index %u",
1451 : : RelationGetRelid(relation));
7627 tgl@sss.pgh.pa.us 1452 :CBC 293894 : oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
1453 : 293894 : relation->rd_indextuple = heap_copytuple(tuple);
1454 : 293894 : relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
1455 : 293894 : MemoryContextSwitchTo(oldcontext);
8090 1456 : 293894 : ReleaseSysCache(tuple);
1457 : :
1458 : : /*
1459 : : * Look up the index's access method, save the OID of its handler function
1460 : : */
863 peter@eisentraut.org 1461 [ - + ]: 293894 : Assert(relation->rd_rel->relam != InvalidOid);
5173 rhaas@postgresql.org 1462 : 293894 : tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
8090 tgl@sss.pgh.pa.us 1463 [ - + ]: 293891 : if (!HeapTupleIsValid(tuple))
7569 tgl@sss.pgh.pa.us 1464 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for access method %u",
1465 : : relation->rd_rel->relam);
3010 tgl@sss.pgh.pa.us 1466 :CBC 293891 : aform = (Form_pg_am) GETSTRUCT(tuple);
1467 : 293891 : relation->rd_amhandler = aform->amhandler;
8090 1468 : 293891 : ReleaseSysCache(tuple);
1469 : :
2199 teodor@sigaev.ru 1470 : 293891 : indnatts = RelationGetNumberOfAttributes(relation);
1471 [ - + ]: 293891 : if (indnatts != IndexRelationGetNumberOfAttributes(relation))
7569 tgl@sss.pgh.pa.us 1472 [ # # ]:UBC 0 : elog(ERROR, "relnatts disagrees with indnatts for index %u",
1473 : : RelationGetRelid(relation));
2199 teodor@sigaev.ru 1474 :CBC 293891 : indnkeyatts = IndexRelationGetNumberOfKeyAttributes(relation);
1475 : :
1476 : : /*
1477 : : * Make the private context to hold index access info. The reason we need
1478 : : * a context, and not just a couple of pallocs, is so that we won't leak
1479 : : * any subsidiary info attached to fmgr lookup records.
1480 : : */
2210 tgl@sss.pgh.pa.us 1481 : 293891 : indexcxt = AllocSetContextCreate(CacheMemoryContext,
1482 : : "index info",
1483 : : ALLOCSET_SMALL_SIZES);
8226 1484 : 293891 : relation->rd_indexcxt = indexcxt;
2200 peter_e@gmx.net 1485 : 293891 : MemoryContextCopyAndSetIdentifier(indexcxt,
1486 : : RelationGetRelationName(relation));
1487 : :
1488 : : /*
1489 : : * Now we can fetch the index AM's API struct
1490 : : */
3010 tgl@sss.pgh.pa.us 1491 : 293891 : InitIndexAmRoutine(relation);
1492 : :
1493 : : /*
1494 : : * Allocate arrays to hold data. Opclasses are not used for included
1495 : : * columns, so allocate them for indnkeyatts only.
1496 : : */
6322 1497 : 293891 : relation->rd_opfamily = (Oid *)
2199 teodor@sigaev.ru 1498 : 293891 : MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
6322 tgl@sss.pgh.pa.us 1499 : 293891 : relation->rd_opcintype = (Oid *)
2199 teodor@sigaev.ru 1500 : 293891 : MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
1501 : :
1910 andres@anarazel.de 1502 : 293891 : amsupport = relation->rd_indam->amsupport;
8353 tgl@sss.pgh.pa.us 1503 [ + - ]: 293891 : if (amsupport > 0)
1504 : : {
1476 akorotkov@postgresql 1505 : 293891 : int nsupport = indnatts * amsupport;
1506 : :
6322 tgl@sss.pgh.pa.us 1507 : 293891 : relation->rd_support = (RegProcedure *)
7462 1508 : 293891 : MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
6322 1509 : 293891 : relation->rd_supportinfo = (FmgrInfo *)
7462 1510 : 293891 : MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
1511 : : }
1512 : : else
1513 : : {
6322 tgl@sss.pgh.pa.us 1514 :UBC 0 : relation->rd_support = NULL;
1515 : 0 : relation->rd_supportinfo = NULL;
1516 : : }
1517 : :
4814 peter_e@gmx.net 1518 :CBC 293891 : relation->rd_indcollation = (Oid *)
2194 teodor@sigaev.ru 1519 : 293891 : MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
1520 : :
6305 tgl@sss.pgh.pa.us 1521 : 293891 : relation->rd_indoption = (int16 *)
2194 teodor@sigaev.ru 1522 : 293891 : MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));
1523 : :
1524 : : /*
1525 : : * indcollation cannot be referenced directly through the C struct,
1526 : : * because it comes after the variable-width indkey field. Must extract
1527 : : * the datum the hard way...
1528 : : */
4814 peter_e@gmx.net 1529 : 293891 : indcollDatum = fastgetattr(relation->rd_indextuple,
1530 : : Anum_pg_index_indcollation,
1531 : : GetPgIndexDescriptor(),
1532 : : &isnull);
1533 [ - + ]: 293891 : Assert(!isnull);
1534 : 293891 : indcoll = (oidvector *) DatumGetPointer(indcollDatum);
2194 teodor@sigaev.ru 1535 : 293891 : memcpy(relation->rd_indcollation, indcoll->values, indnkeyatts * sizeof(Oid));
1536 : :
1537 : : /*
1538 : : * indclass cannot be referenced directly through the C struct, because it
1539 : : * comes after the variable-width indkey field. Must extract the datum
1540 : : * the hard way...
1541 : : */
6322 tgl@sss.pgh.pa.us 1542 : 293891 : indclassDatum = fastgetattr(relation->rd_indextuple,
1543 : : Anum_pg_index_indclass,
1544 : : GetPgIndexDescriptor(),
1545 : : &isnull);
1546 [ - + ]: 293891 : Assert(!isnull);
1547 : 293891 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
1548 : :
1549 : : /*
1550 : : * Fill the support procedure OID array, as well as the info about
1551 : : * opfamilies and opclass input types. (aminfo and supportinfo are left
1552 : : * as zeroes, and are filled on-the-fly when used)
1553 : : */
4885 1554 : 293891 : IndexSupportInitialize(indclass, relation->rd_support,
1555 : : relation->rd_opfamily, relation->rd_opcintype,
1556 : : amsupport, indnkeyatts);
1557 : :
1558 : : /*
1559 : : * Similarly extract indoption and copy it to the cache entry
1560 : : */
6305 1561 : 293891 : indoptionDatum = fastgetattr(relation->rd_indextuple,
1562 : : Anum_pg_index_indoption,
1563 : : GetPgIndexDescriptor(),
1564 : : &isnull);
1565 [ - + ]: 293891 : Assert(!isnull);
1566 : 293891 : indoption = (int2vector *) DatumGetPointer(indoptionDatum);
2194 teodor@sigaev.ru 1567 : 293891 : memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
1568 : :
1476 akorotkov@postgresql 1569 : 293891 : (void) RelationGetIndexAttOptions(relation, false);
1570 : :
1571 : : /*
1572 : : * expressions, predicate, exclusion caches will be filled later
1573 : : */
7627 tgl@sss.pgh.pa.us 1574 : 293888 : relation->rd_indexprs = NIL;
1575 : 293888 : relation->rd_indpred = NIL;
5242 1576 : 293888 : relation->rd_exclops = NULL;
1577 : 293888 : relation->rd_exclprocs = NULL;
1578 : 293888 : relation->rd_exclstrats = NULL;
6564 1579 : 293888 : relation->rd_amcache = NULL;
10141 scrappy@hub.org 1580 : 293888 : }
1581 : :
1582 : : /*
1583 : : * IndexSupportInitialize
1584 : : * Initializes an index's cached opclass information,
1585 : : * given the index's pg_index.indclass entry.
1586 : : *
1587 : : * Data is returned into *indexSupport, *opFamily, and *opcInType,
1588 : : * which are arrays allocated by the caller.
1589 : : *
1590 : : * The caller also passes maxSupportNumber and maxAttributeNumber, since these
1591 : : * indicate the size of the arrays it has allocated --- but in practice these
1592 : : * numbers must always match those obtainable from the system catalog entries
1593 : : * for the index and access method.
1594 : : */
1595 : : static void
6956 tgl@sss.pgh.pa.us 1596 : 293891 : IndexSupportInitialize(oidvector *indclass,
1597 : : RegProcedure *indexSupport,
1598 : : Oid *opFamily,
1599 : : Oid *opcInType,
1600 : : StrategyNumber maxSupportNumber,
1601 : : AttrNumber maxAttributeNumber)
1602 : : {
1603 : : int attIndex;
1604 : :
8090 1605 [ + + ]: 795100 : for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
1606 : : {
1607 : : OpClassCacheEnt *opcentry;
1608 : :
6956 1609 [ - + ]: 501209 : if (!OidIsValid(indclass->values[attIndex]))
7569 tgl@sss.pgh.pa.us 1610 [ # # ]:UBC 0 : elog(ERROR, "bogus pg_index tuple");
1611 : :
1612 : : /* look up the info for this opclass, using a cache */
6956 tgl@sss.pgh.pa.us 1613 :CBC 501209 : opcentry = LookupOpclassInfo(indclass->values[attIndex],
1614 : : maxSupportNumber);
1615 : :
1616 : : /* copy cached data into relcache entry */
6322 1617 : 501209 : opFamily[attIndex] = opcentry->opcfamily;
1618 : 501209 : opcInType[attIndex] = opcentry->opcintype;
8090 1619 [ + - ]: 501209 : if (maxSupportNumber > 0)
1476 akorotkov@postgresql 1620 : 501209 : memcpy(&indexSupport[attIndex * maxSupportNumber],
7462 tgl@sss.pgh.pa.us 1621 : 501209 : opcentry->supportProcs,
1622 : : maxSupportNumber * sizeof(RegProcedure));
1623 : : }
8090 1624 : 293891 : }
1625 : :
1626 : : /*
1627 : : * LookupOpclassInfo
1628 : : *
1629 : : * This routine maintains a per-opclass cache of the information needed
1630 : : * by IndexSupportInitialize(). This is more efficient than relying on
1631 : : * the catalog cache, because we can load all the info about a particular
1632 : : * opclass in a single indexscan of pg_amproc.
1633 : : *
1634 : : * The information from pg_am about expected range of support function
1635 : : * numbers is passed in, rather than being looked up, mainly because the
1636 : : * caller will have it already.
1637 : : *
1638 : : * Note there is no provision for flushing the cache. This is OK at the
1639 : : * moment because there is no way to ALTER any interesting properties of an
1640 : : * existing opclass --- all you can do is drop it, which will result in
1641 : : * a useless but harmless dead entry in the cache. To support altering
1642 : : * opclass membership (not the same as opfamily membership!), we'd need to
1643 : : * be able to flush this cache as well as the contents of relcache entries
1644 : : * for indexes.
1645 : : */
1646 : : static OpClassCacheEnt *
1647 : 501209 : LookupOpclassInfo(Oid operatorClassOid,
1648 : : StrategyNumber numSupport)
1649 : : {
1650 : : OpClassCacheEnt *opcentry;
1651 : : bool found;
1652 : : Relation rel;
1653 : : SysScanDesc scan;
1654 : : ScanKeyData skey[3];
1655 : : HeapTuple htup;
1656 : : bool indexOK;
1657 : :
1658 [ + + ]: 501209 : if (OpClassCache == NULL)
1659 : : {
1660 : : /* First time through: initialize the opclass cache */
1661 : : HASHCTL ctl;
1662 : :
1663 : : /* Also make sure CacheMemoryContext exists */
1014 1664 [ - + ]: 13249 : if (!CacheMemoryContext)
1014 tgl@sss.pgh.pa.us 1665 :UBC 0 : CreateCacheMemoryContext();
1666 : :
8090 tgl@sss.pgh.pa.us 1667 :CBC 13249 : ctl.keysize = sizeof(Oid);
1668 : 13249 : ctl.entrysize = sizeof(OpClassCacheEnt);
1669 : 13249 : OpClassCache = hash_create("Operator class cache", 64,
1670 : : &ctl, HASH_ELEM | HASH_BLOBS);
1671 : : }
1672 : :
1673 : 501209 : opcentry = (OpClassCacheEnt *) hash_search(OpClassCache,
1674 : : &operatorClassOid,
1675 : : HASH_ENTER, &found);
1676 : :
5982 1677 [ + + ]: 501209 : if (!found)
1678 : : {
1679 : : /* Initialize new entry */
1680 : 37395 : opcentry->valid = false; /* until known OK */
1681 : 37395 : opcentry->numSupport = numSupport;
1014 1682 : 37395 : opcentry->supportProcs = NULL; /* filled below */
1683 : : }
1684 : : else
1685 : : {
8090 1686 [ - + ]: 463814 : Assert(numSupport == opcentry->numSupport);
1687 : : }
1688 : :
1689 : : /*
1690 : : * When aggressively testing cache-flush hazards, we disable the operator
1691 : : * class cache and force reloading of the info on each call. This models
1692 : : * no real-world behavior, since the cache entries are never invalidated
1693 : : * otherwise. However it can be helpful for detecting bugs in the cache
1694 : : * loading logic itself, such as reliance on a non-nailed index. Given
1695 : : * the limited use-case and the fact that this adds a great deal of
1696 : : * expense, we enable it only for high values of debug_discard_caches.
1697 : : */
1698 : : #ifdef DISCARD_CACHES_ENABLED
1006 1699 [ - + ]: 501209 : if (debug_discard_caches > 2)
1194 peter@eisentraut.org 1700 :UBC 0 : opcentry->valid = false;
1701 : : #endif
1702 : :
5982 tgl@sss.pgh.pa.us 1703 [ + + ]:CBC 501209 : if (opcentry->valid)
1704 : 463814 : return opcentry;
1705 : :
1706 : : /*
1707 : : * Need to fill in new entry. First allocate space, unless we already did
1708 : : * so in some previous attempt.
1709 : : */
1014 1710 [ + - + - ]: 37395 : if (opcentry->supportProcs == NULL && numSupport > 0)
1711 : 37395 : opcentry->supportProcs = (RegProcedure *)
1712 : 37395 : MemoryContextAllocZero(CacheMemoryContext,
1713 : : numSupport * sizeof(RegProcedure));
1714 : :
1715 : : /*
1716 : : * To avoid infinite recursion during startup, force heap scans if we're
1717 : : * looking up info for the opclasses used by the indexes we would like to
1718 : : * reference here.
1719 : : */
8090 1720 [ + + ]: 41933 : indexOK = criticalRelcachesBuilt ||
1721 [ + + ]: 4538 : (operatorClassOid != OID_BTREE_OPS_OID &&
1722 [ + + ]: 3103 : operatorClassOid != INT2_BTREE_OPS_OID);
1723 : :
1724 : : /*
1725 : : * We have to fetch the pg_opclass row to determine its opfamily and
1726 : : * opcintype, which are needed to look up related operators and functions.
1727 : : * It'd be convenient to use the syscache here, but that probably doesn't
1728 : : * work while bootstrapping.
1729 : : */
6322 1730 : 37395 : ScanKeyInit(&skey[0],
1731 : : Anum_pg_opclass_oid,
1732 : : BTEqualStrategyNumber, F_OIDEQ,
1733 : : ObjectIdGetDatum(operatorClassOid));
1910 andres@anarazel.de 1734 : 37395 : rel = table_open(OperatorClassRelationId, AccessShareLock);
6322 tgl@sss.pgh.pa.us 1735 : 37395 : scan = systable_beginscan(rel, OpclassOidIndexId, indexOK,
1736 : : NULL, 1, skey);
1737 : :
1738 [ + - ]: 37395 : if (HeapTupleIsValid(htup = systable_getnext(scan)))
1739 : : {
1740 : 37395 : Form_pg_opclass opclassform = (Form_pg_opclass) GETSTRUCT(htup);
1741 : :
1742 : 37395 : opcentry->opcfamily = opclassform->opcfamily;
1743 : 37395 : opcentry->opcintype = opclassform->opcintype;
1744 : : }
1745 : : else
6322 tgl@sss.pgh.pa.us 1746 [ # # ]:UBC 0 : elog(ERROR, "could not find tuple for opclass %u", operatorClassOid);
1747 : :
6322 tgl@sss.pgh.pa.us 1748 :CBC 37395 : systable_endscan(scan);
1910 andres@anarazel.de 1749 : 37395 : table_close(rel, AccessShareLock);
1750 : :
1751 : : /*
1752 : : * Scan pg_amproc to obtain support procs for the opclass. We only fetch
1753 : : * the default ones (those with lefttype = righttype = opcintype).
1754 : : */
8090 tgl@sss.pgh.pa.us 1755 [ + - ]: 37395 : if (numSupport > 0)
1756 : : {
7459 1757 : 37395 : ScanKeyInit(&skey[0],
1758 : : Anum_pg_amproc_amprocfamily,
1759 : : BTEqualStrategyNumber, F_OIDEQ,
1760 : : ObjectIdGetDatum(opcentry->opcfamily));
1761 : 37395 : ScanKeyInit(&skey[1],
1762 : : Anum_pg_amproc_amproclefttype,
1763 : : BTEqualStrategyNumber, F_OIDEQ,
1764 : : ObjectIdGetDatum(opcentry->opcintype));
6322 1765 : 37395 : ScanKeyInit(&skey[2],
1766 : : Anum_pg_amproc_amprocrighttype,
1767 : : BTEqualStrategyNumber, F_OIDEQ,
1768 : : ObjectIdGetDatum(opcentry->opcintype));
1910 andres@anarazel.de 1769 : 37395 : rel = table_open(AccessMethodProcedureRelationId, AccessShareLock);
6940 tgl@sss.pgh.pa.us 1770 : 37395 : scan = systable_beginscan(rel, AccessMethodProcedureIndexId, indexOK,
1771 : : NULL, 3, skey);
1772 : :
7459 1773 [ + + ]: 154927 : while (HeapTupleIsValid(htup = systable_getnext(scan)))
1774 : : {
8090 1775 : 117532 : Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
1776 : :
1476 akorotkov@postgresql 1777 [ + - ]: 117532 : if (amprocform->amprocnum <= 0 ||
8090 tgl@sss.pgh.pa.us 1778 [ - + ]: 117532 : (StrategyNumber) amprocform->amprocnum > numSupport)
7569 tgl@sss.pgh.pa.us 1779 [ # # ]:UBC 0 : elog(ERROR, "invalid amproc number %d for opclass %u",
1780 : : amprocform->amprocnum, operatorClassOid);
1781 : :
1476 akorotkov@postgresql 1782 :CBC 117532 : opcentry->supportProcs[amprocform->amprocnum - 1] =
1783 : 117532 : amprocform->amproc;
1784 : : }
1785 : :
7459 tgl@sss.pgh.pa.us 1786 : 37395 : systable_endscan(scan);
1910 andres@anarazel.de 1787 : 37395 : table_close(rel, AccessShareLock);
1788 : : }
1789 : :
8090 tgl@sss.pgh.pa.us 1790 : 37395 : opcentry->valid = true;
1791 : 37395 : return opcentry;
1792 : : }
1793 : :
1794 : : /*
1795 : : * Fill in the TableAmRoutine for a relation
1796 : : *
1797 : : * relation's rd_amhandler must be valid already.
1798 : : */
1799 : : static void
1866 andres@anarazel.de 1800 : 959362 : InitTableAmRoutine(Relation relation)
1801 : : {
1802 : 959362 : relation->rd_tableam = GetTableAmRoutine(relation->rd_amhandler);
1803 : 959362 : }
1804 : :
1805 : : /*
1806 : : * Initialize table access method support for a table like relation
1807 : : */
1808 : : void
1809 : 959362 : RelationInitTableAccessMethod(Relation relation)
1810 : : {
1811 : : HeapTuple tuple;
1812 : : Form_pg_am aform;
1813 : :
1814 [ + + ]: 959362 : if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
1815 : : {
1816 : : /*
1817 : : * Sequences are currently accessed like heap tables, but it doesn't
1818 : : * seem prudent to show that in the catalog. So just overwrite it
1819 : : * here.
1820 : : */
863 peter@eisentraut.org 1821 [ - + ]: 3440 : Assert(relation->rd_rel->relam == InvalidOid);
1264 tgl@sss.pgh.pa.us 1822 : 3440 : relation->rd_amhandler = F_HEAP_TABLEAM_HANDLER;
1823 : : }
1866 andres@anarazel.de 1824 [ + + ]: 955922 : else if (IsCatalogRelation(relation))
1825 : : {
1826 : : /*
1827 : : * Avoid doing a syscache lookup for catalog tables.
1828 : : */
1829 [ - + ]: 762535 : Assert(relation->rd_rel->relam == HEAP_TABLE_AM_OID);
1264 tgl@sss.pgh.pa.us 1830 : 762535 : relation->rd_amhandler = F_HEAP_TABLEAM_HANDLER;
1831 : : }
1832 : : else
1833 : : {
1834 : : /*
1835 : : * Look up the table access method, save the OID of its handler
1836 : : * function.
1837 : : */
1866 andres@anarazel.de 1838 [ - + ]: 193387 : Assert(relation->rd_rel->relam != InvalidOid);
1839 : 193387 : tuple = SearchSysCache1(AMOID,
1840 : 193387 : ObjectIdGetDatum(relation->rd_rel->relam));
1841 [ - + ]: 193387 : if (!HeapTupleIsValid(tuple))
1866 andres@anarazel.de 1842 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for access method %u",
1843 : : relation->rd_rel->relam);
1866 andres@anarazel.de 1844 :CBC 193387 : aform = (Form_pg_am) GETSTRUCT(tuple);
1845 : 193387 : relation->rd_amhandler = aform->amhandler;
1846 : 193387 : ReleaseSysCache(tuple);
1847 : : }
1848 : :
1849 : : /*
1850 : : * Now we can fetch the table AM's API struct
1851 : : */
1852 : 959362 : InitTableAmRoutine(relation);
1853 : 959362 : }
1854 : :
1855 : : /*
1856 : : * formrdesc
1857 : : *
1858 : : * This is a special cut-down version of RelationBuildDesc(),
1859 : : * used while initializing the relcache.
1860 : : * The relation descriptor is built just from the supplied parameters,
1861 : : * without actually looking at any system table entries. We cheat
1862 : : * quite a lot since we only need to work for a few basic system
1863 : : * catalogs.
1864 : : *
1865 : : * The catalogs this is used for can't have constraints (except attnotnull),
1866 : : * default values, rules, or triggers, since we don't cope with any of that.
1867 : : * (Well, actually, this only matters for properties that need to be valid
1868 : : * during bootstrap or before RelationCacheInitializePhase3 runs, and none of
1869 : : * these properties matter then...)
1870 : : *
1871 : : * NOTE: we assume we are already switched into CacheMemoryContext.
1872 : : */
1873 : : static void
5314 tgl@sss.pgh.pa.us 1874 : 20272 : formrdesc(const char *relationName, Oid relationReltype,
1875 : : bool isshared,
1876 : : int natts, const FormData_pg_attribute *attrs)
1877 : : {
1878 : : Relation relation;
1879 : : int i;
1880 : : bool has_not_null;
1881 : :
1882 : : /*
1883 : : * allocate new relation desc, clear all fields of reldesc
1884 : : */
7823 bruce@momjian.us 1885 : 20272 : relation = (Relation) palloc0(sizeof(RelationData));
1886 : :
1887 : : /* make sure relation is marked as having no open file yet */
7369 tgl@sss.pgh.pa.us 1888 : 20272 : relation->rd_smgr = NULL;
1889 : :
1890 : : /*
1891 : : * initialize reference count: 1 because it is nailed in cache
1892 : : */
7211 1893 : 20272 : relation->rd_refcnt = 1;
1894 : :
1895 : : /*
1896 : : * all entries built with this routine are nailed-in-cache; none are for
1897 : : * new or temp relations.
1898 : : */
7169 1899 : 20272 : relation->rd_isnailed = true;
7150 1900 : 20272 : relation->rd_createSubid = InvalidSubTransactionId;
648 rhaas@postgresql.org 1901 : 20272 : relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
1902 : 20272 : relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1471 noah@leadboat.com 1903 : 20272 : relation->rd_droppedSubid = InvalidSubTransactionId;
42 heikki.linnakangas@i 1904 :GNC 20272 : relation->rd_backend = INVALID_PROC_NUMBER;
4136 tgl@sss.pgh.pa.us 1905 :CBC 20272 : relation->rd_islocaltemp = false;
1906 : :
1907 : : /*
1908 : : * initialize relation tuple form
1909 : : *
1910 : : * The data we insert here is pretty incomplete/bogus, but it'll serve to
1911 : : * get us launched. RelationCacheInitializePhase3() will read the real
1912 : : * data from pg_class and replace what we've done here. Note in
1913 : : * particular that relowner is left as zero; this cues
1914 : : * RelationCacheInitializePhase3 that the real data isn't there yet.
1915 : : */
7823 bruce@momjian.us 1916 : 20272 : relation->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);
1917 : :
8055 tgl@sss.pgh.pa.us 1918 : 20272 : namestrcpy(&relation->rd_rel->relname, relationName);
1919 : 20272 : relation->rd_rel->relnamespace = PG_CATALOG_NAMESPACE;
5314 1920 : 20272 : relation->rd_rel->reltype = relationReltype;
1921 : :
1922 : : /*
1923 : : * It's important to distinguish between shared and non-shared relations,
1924 : : * even at bootstrap time, to make sure we know where they are stored.
1925 : : */
5359 1926 : 20272 : relation->rd_rel->relisshared = isshared;
1927 [ + + ]: 20272 : if (isshared)
1928 : 14520 : relation->rd_rel->reltablespace = GLOBALTABLESPACE_OID;
1929 : :
1930 : : /* formrdesc is used only for permanent relations */
4871 rhaas@postgresql.org 1931 : 20272 : relation->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;
1932 : :
1933 : : /* ... and they're always populated, too */
3996 tgl@sss.pgh.pa.us 1934 : 20272 : relation->rd_rel->relispopulated = true;
1935 : :
3810 rhaas@postgresql.org 1936 : 20272 : relation->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING;
4611 tgl@sss.pgh.pa.us 1937 : 20272 : relation->rd_rel->relpages = 0;
1323 1938 : 20272 : relation->rd_rel->reltuples = -1;
4566 1939 : 20272 : relation->rd_rel->relallvisible = 0;
9716 bruce@momjian.us 1940 : 20272 : relation->rd_rel->relkind = RELKIND_RELATION;
8689 tgl@sss.pgh.pa.us 1941 : 20272 : relation->rd_rel->relnatts = (int16) natts;
1866 andres@anarazel.de 1942 : 20272 : relation->rd_rel->relam = HEAP_TABLE_AM_OID;
1943 : :
1944 : : /*
1945 : : * initialize attribute tuple form
1946 : : *
1947 : : * Unlike the case with the relation tuple, this data had better be right
1948 : : * because it will never be replaced. The data comes from
1949 : : * src/include/catalog/ headers via genbki.pl.
1950 : : */
1972 1951 : 20272 : relation->rd_att = CreateTemplateTupleDesc(natts);
6512 tgl@sss.pgh.pa.us 1952 : 20272 : relation->rd_att->tdrefcount = 1; /* mark as refcounted */
1953 : :
5314 1954 : 20272 : relation->rd_att->tdtypeid = relationReltype;
1377 1955 : 20272 : relation->rd_att->tdtypmod = -1; /* just to be sure */
1956 : :
1957 : : /*
1958 : : * initialize tuple desc info
1959 : : */
7821 1960 : 20272 : has_not_null = false;
9716 bruce@momjian.us 1961 [ + + ]: 365606 : for (i = 0; i < natts; i++)
1962 : : {
2429 andres@anarazel.de 1963 : 345334 : memcpy(TupleDescAttr(relation->rd_att, i),
5359 tgl@sss.pgh.pa.us 1964 : 345334 : &attrs[i],
1965 : : ATTRIBUTE_FIXED_PART_SIZE);
1966 : 345334 : has_not_null |= attrs[i].attnotnull;
1967 : : /* make sure attcacheoff is valid */
2429 andres@anarazel.de 1968 : 345334 : TupleDescAttr(relation->rd_att, i)->attcacheoff = -1;
1969 : : }
1970 : :
1971 : : /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */
1972 : 20272 : TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0;
1973 : :
1974 : : /* mark not-null status */
7821 tgl@sss.pgh.pa.us 1975 [ + - ]: 20272 : if (has_not_null)
1976 : : {
1977 : 20272 : TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1978 : :
1979 : 20272 : constr->has_not_null = true;
1980 : 20272 : relation->rd_att->constr = constr;
1981 : : }
1982 : :
1983 : : /*
1984 : : * initialize relation id from info in att array (my, this is ugly)
1985 : : */
2429 andres@anarazel.de 1986 : 20272 : RelationGetRelid(relation) = TupleDescAttr(relation->rd_att, 0)->attrelid;
1987 : :
1988 : : /*
1989 : : * All relations made with formrdesc are mapped. This is necessarily so
1990 : : * because there is no other way to know what filenumber they currently
1991 : : * have. In bootstrap mode, add them to the initial relation mapper data,
1992 : : * specifying that the initial filenumber is the same as the OID.
1993 : : */
648 rhaas@postgresql.org 1994 : 20272 : relation->rd_rel->relfilenode = InvalidRelFileNumber;
5180 tgl@sss.pgh.pa.us 1995 [ + + ]: 20272 : if (IsBootstrapProcessingMode())
1996 : 156 : RelationMapUpdateMap(RelationGetRelid(relation),
1997 : : RelationGetRelid(relation),
1998 : : isshared, true);
1999 : :
2000 : : /*
2001 : : * initialize the relation lock manager information
2002 : : */
2489 2003 : 20272 : RelationInitLockInfo(relation); /* see lmgr.c */
2004 : :
2005 : : /*
2006 : : * initialize physical addressing information for the relation
2007 : : */
7240 2008 : 20272 : RelationInitPhysicalAddr(relation);
2009 : :
2010 : : /*
2011 : : * initialize the table am handler
2012 : : */
1866 andres@anarazel.de 2013 : 20272 : relation->rd_rel->relam = HEAP_TABLE_AM_OID;
2014 : 20272 : relation->rd_tableam = GetHeapamTableAmRoutine();
2015 : :
2016 : : /*
2017 : : * initialize the rel-has-index flag, using hardwired knowledge
2018 : : */
7063 tgl@sss.pgh.pa.us 2019 [ + + ]: 20272 : if (IsBootstrapProcessingMode())
2020 : : {
2021 : : /* In bootstrap mode, we have no indexes */
2022 : 156 : relation->rd_rel->relhasindex = false;
2023 : : }
2024 : : else
2025 : : {
2026 : : /* Otherwise, all the rels formrdesc is used for have indexes */
8023 2027 : 20116 : relation->rd_rel->relhasindex = true;
2028 : : }
2029 : :
2030 : : /*
2031 : : * add new reldesc to relcache
2032 : : */
3619 2033 [ - + ]: 20272 : RelationCacheInsert(relation, false);
2034 : :
2035 : : /* It's fully valid */
7169 2036 : 20272 : relation->rd_isvalid = true;
10141 scrappy@hub.org 2037 : 20272 : }
2038 : :
2039 : :
2040 : : /* ----------------------------------------------------------------
2041 : : * Relation Descriptor Lookup Interface
2042 : : * ----------------------------------------------------------------
2043 : : */
2044 : :
2045 : : /*
2046 : : * RelationIdGetRelation
2047 : : *
2048 : : * Lookup a reldesc by OID; make one if not already in cache.
2049 : : *
2050 : : * Returns NULL if no pg_class row could be found for the given relid
2051 : : * (suggesting we are trying to access a just-deleted relation).
2052 : : * Any other error is reported via elog.
2053 : : *
2054 : : * NB: caller should already have at least AccessShareLock on the
2055 : : * relation ID, else there are nasty race conditions.
2056 : : *
2057 : : * NB: relation ref count is incremented, or set to 1 if new entry.
2058 : : * Caller should eventually decrement count. (Usually,
2059 : : * that happens by calling RelationClose().)
2060 : : */
2061 : : Relation
6467 tgl@sss.pgh.pa.us 2062 : 17998843 : RelationIdGetRelation(Oid relationId)
2063 : : {
2064 : : Relation rd;
2065 : :
2066 : : /* Make sure we're in an xact, even if this ends up being a cache hit */
3720 2067 [ - + ]: 17998843 : Assert(IsTransactionState());
2068 : :
2069 : : /*
2070 : : * first try to find reldesc in the cache
2071 : : */
9716 bruce@momjian.us 2072 [ + + ]: 17998843 : RelationIdCacheLookup(relationId, rd);
2073 : :
2074 [ + + ]: 17998842 : if (RelationIsValid(rd))
2075 : : {
2076 : : /* return NULL for dropped relations */
1471 noah@leadboat.com 2077 [ + + ]: 17491245 : if (rd->rd_droppedSubid != InvalidSubTransactionId)
2078 : : {
2079 [ - + ]: 58 : Assert(!rd->rd_isvalid);
2080 : 58 : return NULL;
2081 : : }
2082 : :
9716 bruce@momjian.us 2083 : 17491187 : RelationIncrementReferenceCount(rd);
2084 : : /* revalidate cache entry if necessary */
7169 tgl@sss.pgh.pa.us 2085 [ + + ]: 17491187 : if (!rd->rd_isvalid)
2086 : : {
2087 : : /*
2088 : : * Indexes only have a limited number of possible schema changes,
2089 : : * and we don't want to use the full-blown procedure because it's
2090 : : * a headache for indexes that reload itself depends on.
2091 : : */
2277 alvherre@alvh.no-ip. 2092 [ + + ]: 68357 : if (rd->rd_rel->relkind == RELKIND_INDEX ||
2093 [ - + ]: 57475 : rd->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5206 tgl@sss.pgh.pa.us 2094 : 10882 : RelationReloadIndexInfo(rd);
2095 : : else
2096 : 57475 : RelationClearRelation(rd, true);
2097 : :
2098 : : /*
2099 : : * Normally entries need to be valid here, but before the relcache
2100 : : * has been initialized, not enough infrastructure exists to
2101 : : * perform pg_class lookups. The structure of such entries doesn't
2102 : : * change, but we still want to update the rd_rel entry. So
2103 : : * rd_isvalid = false is left in place for a later lookup.
2104 : : */
2133 andres@anarazel.de 2105 [ + + + - : 68351 : Assert(rd->rd_isvalid ||
- + ]
2106 : : (rd->rd_isnailed && !criticalRelcachesBuilt));
2107 : : }
9716 bruce@momjian.us 2108 : 17491181 : return rd;
2109 : : }
2110 : :
2111 : : /*
2112 : : * no reldesc in the cache, so have RelationBuildDesc() build one and add
2113 : : * it.
2114 : : */
5206 tgl@sss.pgh.pa.us 2115 : 507597 : rd = RelationBuildDesc(relationId, true);
7211 2116 [ + + ]: 507594 : if (RelationIsValid(rd))
2117 : 506923 : RelationIncrementReferenceCount(rd);
10141 scrappy@hub.org 2118 : 507594 : return rd;
2119 : : }
2120 : :
2121 : : /* ----------------------------------------------------------------
2122 : : * cache invalidation support routines
2123 : : * ----------------------------------------------------------------
2124 : : */
2125 : :
2126 : : /* ResourceOwner callbacks to track relcache references */
2127 : : static void ResOwnerReleaseRelation(Datum res);
2128 : : static char *ResOwnerPrintRelCache(Datum res);
2129 : :
2130 : : static const ResourceOwnerDesc relref_resowner_desc =
2131 : : {
2132 : : .name = "relcache reference",
2133 : : .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
2134 : : .release_priority = RELEASE_PRIO_RELCACHE_REFS,
2135 : : .ReleaseResource = ResOwnerReleaseRelation,
2136 : : .DebugPrint = ResOwnerPrintRelCache
2137 : : };
2138 : :
2139 : : /* Convenience wrappers over ResourceOwnerRemember/Forget */
2140 : : static inline void
158 heikki.linnakangas@i 2141 :GNC 26340646 : ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
2142 : : {
2143 : 26340646 : ResourceOwnerRemember(owner, PointerGetDatum(rel), &relref_resowner_desc);
2144 : 26340646 : }
2145 : : static inline void
2146 : 26323015 : ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
2147 : : {
2148 : 26323015 : ResourceOwnerForget(owner, PointerGetDatum(rel), &relref_resowner_desc);
2149 : 26323015 : }
2150 : :
2151 : : /*
2152 : : * RelationIncrementReferenceCount
2153 : : * Increments relation reference count.
2154 : : *
2155 : : * Note: bootstrap mode has its own weird ideas about relation refcount
2156 : : * behavior; we ought to fix it someday, but for now, just disable
2157 : : * reference count ownership tracking in bootstrap mode.
2158 : : */
2159 : : void
7211 tgl@sss.pgh.pa.us 2160 :CBC 26562932 : RelationIncrementReferenceCount(Relation rel)
2161 : : {
158 heikki.linnakangas@i 2162 :GNC 26562932 : ResourceOwnerEnlarge(CurrentResourceOwner);
7211 tgl@sss.pgh.pa.us 2163 :CBC 26562932 : rel->rd_refcnt += 1;
2164 [ + + ]: 26562932 : if (!IsBootstrapProcessingMode())
2165 : 26340646 : ResourceOwnerRememberRelationRef(CurrentResourceOwner, rel);
2166 : 26562932 : }
2167 : :
2168 : : /*
2169 : : * RelationDecrementReferenceCount
2170 : : * Decrements relation reference count.
2171 : : */
2172 : : void
2173 : 26545301 : RelationDecrementReferenceCount(Relation rel)
2174 : : {
2175 [ - + ]: 26545301 : Assert(rel->rd_refcnt > 0);
2176 : 26545301 : rel->rd_refcnt -= 1;
2177 [ + + ]: 26545301 : if (!IsBootstrapProcessingMode())
2178 : 26323015 : ResourceOwnerForgetRelationRef(CurrentResourceOwner, rel);
2179 : 26545301 : }
2180 : :
2181 : : /*
2182 : : * RelationClose - close an open relation
2183 : : *
2184 : : * Actually, we just decrement the refcount.
2185 : : *
2186 : : * NOTE: if compiled with -DRELCACHE_FORCE_RELEASE then relcache entries
2187 : : * will be freed as soon as their refcount goes to zero. In combination
2188 : : * with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
2189 : : * to catch references to already-released relcache entries. It slows
2190 : : * things down quite a bit, however.
2191 : : */
2192 : : void
10141 scrappy@hub.org 2193 : 18041740 : RelationClose(Relation relation)
2194 : : {
2195 : : /* Note: no locking manipulations needed */
9716 bruce@momjian.us 2196 : 18041740 : RelationDecrementReferenceCount(relation);
2197 : :
158 heikki.linnakangas@i 2198 :GNC 18041740 : RelationCloseCleanup(relation);
2199 : 18041740 : }
2200 : :
2201 : : static void
2202 : 18059365 : RelationCloseCleanup(Relation relation)
2203 : : {
2204 : : /*
2205 : : * If the relation is no longer open in this session, we can clean up any
2206 : : * stale partition descriptors it has. This is unlikely, so check to see
2207 : : * if there are child contexts before expending a call to mcxt.c.
2208 : : */
1082 alvherre@alvh.no-ip. 2209 [ + + ]:CBC 18059365 : if (RelationHasReferenceCountZero(relation))
2210 : : {
2211 [ + + ]: 10380810 : if (relation->rd_pdcxt != NULL &&
2212 [ + + ]: 47234 : relation->rd_pdcxt->firstchild != NULL)
2213 : 1997 : MemoryContextDeleteChildren(relation->rd_pdcxt);
2214 : :
2215 [ + + ]: 10380810 : if (relation->rd_pddcxt != NULL &&
2216 [ - + ]: 47 : relation->rd_pddcxt->firstchild != NULL)
1082 alvherre@alvh.no-ip. 2217 :UBC 0 : MemoryContextDeleteChildren(relation->rd_pddcxt);
2218 : : }
2219 : :
2220 : : #ifdef RELCACHE_FORCE_RELEASE
2221 : : if (RelationHasReferenceCountZero(relation) &&
2222 : : relation->rd_createSubid == InvalidSubTransactionId &&
2223 : : relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId)
2224 : : RelationClearRelation(relation, false);
2225 : : #endif
10141 scrappy@hub.org 2226 :CBC 18059365 : }
2227 : :
2228 : : /*
2229 : : * RelationReloadIndexInfo - reload minimal information for an open index
2230 : : *
2231 : : * This function is used only for indexes. A relcache inval on an index
2232 : : * can mean that its pg_class or pg_index row changed. There are only
2233 : : * very limited changes that are allowed to an existing index's schema,
2234 : : * so we can update the relcache entry without a complete rebuild; which
2235 : : * is fortunate because we can't rebuild an index entry that is "nailed"
2236 : : * and/or in active use. We support full replacement of the pg_class row,
2237 : : * as well as updates of a few simple fields of the pg_index row.
2238 : : *
2239 : : * We can't necessarily reread the catalog rows right away; we might be
2240 : : * in a failed transaction when we receive the SI notification. If so,
2241 : : * RelationClearRelation just marks the entry as invalid by setting
2242 : : * rd_isvalid to false. This routine is called to fix the entry when it
2243 : : * is next needed.
2244 : : *
2245 : : * We assume that at the time we are called, we have at least AccessShareLock
2246 : : * on the target index. (Note: in the calls from RelationClearRelation,
2247 : : * this is legitimate because we know the rel has positive refcount.)
2248 : : *
2249 : : * If the target index is an index on pg_class or pg_index, we'd better have
2250 : : * previously gotten at least AccessShareLock on its underlying catalog,
2251 : : * else we are at risk of deadlock against someone trying to exclusive-lock
2252 : : * the heap and index in that order. This is ensured in current usage by
2253 : : * only applying this to indexes being opened or having positive refcount.
2254 : : */
2255 : : static void
6192 tgl@sss.pgh.pa.us 2256 : 34684 : RelationReloadIndexInfo(Relation relation)
2257 : : {
2258 : : bool indexOK;
2259 : : HeapTuple pg_class_tuple;
2260 : : Form_pg_class relp;
2261 : :
2262 : : /* Should be called only for invalidated, live indexes */
2277 alvherre@alvh.no-ip. 2263 [ + + + - : 34684 : Assert((relation->rd_rel->relkind == RELKIND_INDEX ||
+ - - + ]
2264 : : relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) &&
2265 : : !relation->rd_isvalid &&
2266 : : relation->rd_droppedSubid == InvalidSubTransactionId);
2267 : :
2268 : : /* Ensure it's closed at smgr level */
3720 tgl@sss.pgh.pa.us 2269 : 34684 : RelationCloseSmgr(relation);
2270 : :
2271 : : /* Must free any AM cached data upon relcache flush */
3 akorotkov@postgresql 2272 [ - + ]: 34684 : if (relation->rd_amcache)
3 akorotkov@postgresql 2273 :UBC 0 : pfree(relation->rd_amcache);
3 akorotkov@postgresql 2274 :CBC 34684 : relation->rd_amcache = NULL;
2275 : :
2276 : : /*
2277 : : * If it's a shared index, we might be called before backend startup has
2278 : : * finished selecting a database, in which case we have no way to read
2279 : : * pg_class yet. However, a shared index can never have any significant
2280 : : * schema updates, so it's okay to ignore the invalidation signal. Just
2281 : : * mark it valid and return without doing anything more.
2282 : : */
5359 tgl@sss.pgh.pa.us 2283 [ + + - + ]: 34684 : if (relation->rd_rel->relisshared && !criticalRelcachesBuilt)
2284 : : {
5359 tgl@sss.pgh.pa.us 2285 :UBC 0 : relation->rd_isvalid = true;
2286 : 0 : return;
2287 : : }
2288 : :
2289 : : /*
2290 : : * Read the pg_class row
2291 : : *
2292 : : * Don't try to use an indexscan of pg_class_oid_index to reload the info
2293 : : * for pg_class_oid_index ...
2294 : : */
6940 tgl@sss.pgh.pa.us 2295 :CBC 34684 : indexOK = (RelationGetRelid(relation) != ClassOidIndexId);
3695 rhaas@postgresql.org 2296 : 34684 : pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), indexOK, false);
8528 inoue@tpf.co.jp 2297 [ - + ]: 34681 : if (!HeapTupleIsValid(pg_class_tuple))
6660 tgl@sss.pgh.pa.us 2298 [ # # ]:UBC 0 : elog(ERROR, "could not find pg_class tuple for index %u",
2299 : : RelationGetRelid(relation));
8528 inoue@tpf.co.jp 2300 :CBC 34681 : relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
6660 tgl@sss.pgh.pa.us 2301 : 34681 : memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE);
2302 : : /* Reload reloptions in case they changed */
6496 bruce@momjian.us 2303 [ + + ]: 34681 : if (relation->rd_options)
2304 : 255 : pfree(relation->rd_options);
6495 tgl@sss.pgh.pa.us 2305 : 34681 : RelationParseRelOptions(relation, pg_class_tuple);
2306 : : /* done with pg_class tuple */
8528 inoue@tpf.co.jp 2307 : 34681 : heap_freetuple(pg_class_tuple);
2308 : : /* We must recalculate physical address in case it changed */
6660 tgl@sss.pgh.pa.us 2309 : 34681 : RelationInitPhysicalAddr(relation);
2310 : :
2311 : : /*
2312 : : * For a non-system index, there are fields of the pg_index row that are
2313 : : * allowed to change, so re-read that row and update the relcache entry.
2314 : : * Most of the info derived from pg_index (such as support function lookup
2315 : : * info) cannot change, and indeed the whole point of this routine is to
2316 : : * update the relcache entry without clobbering that data; so wholesale
2317 : : * replacement is not appropriate.
2318 : : */
6192 2319 [ + + ]: 34681 : if (!IsSystemRelation(relation))
2320 : : {
2321 : : HeapTuple tuple;
2322 : : Form_pg_index index;
2323 : :
5173 rhaas@postgresql.org 2324 : 11964 : tuple = SearchSysCache1(INDEXRELID,
2325 : : ObjectIdGetDatum(RelationGetRelid(relation)));
6192 tgl@sss.pgh.pa.us 2326 [ - + ]: 11964 : if (!HeapTupleIsValid(tuple))
5995 bruce@momjian.us 2327 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for index %u",
2328 : : RelationGetRelid(relation));
6192 tgl@sss.pgh.pa.us 2329 :CBC 11964 : index = (Form_pg_index) GETSTRUCT(tuple);
2330 : :
2331 : : /*
2332 : : * Basically, let's just copy all the bool fields. There are one or
2333 : : * two of these that can't actually change in the current code, but
2334 : : * it's not worth it to track exactly which ones they are. None of
2335 : : * the array fields are allowed to change, though.
2336 : : */
4155 2337 : 11964 : relation->rd_index->indisunique = index->indisunique;
801 peter@eisentraut.org 2338 : 11964 : relation->rd_index->indnullsnotdistinct = index->indnullsnotdistinct;
4155 tgl@sss.pgh.pa.us 2339 : 11964 : relation->rd_index->indisprimary = index->indisprimary;
2340 : 11964 : relation->rd_index->indisexclusion = index->indisexclusion;
2341 : 11964 : relation->rd_index->indimmediate = index->indimmediate;
2342 : 11964 : relation->rd_index->indisclustered = index->indisclustered;
6192 2343 : 11964 : relation->rd_index->indisvalid = index->indisvalid;
6051 2344 : 11964 : relation->rd_index->indcheckxmin = index->indcheckxmin;
2345 : 11964 : relation->rd_index->indisready = index->indisready;
4155 2346 : 11964 : relation->rd_index->indislive = index->indislive;
275 michael@paquier.xyz 2347 : 11964 : relation->rd_index->indisreplident = index->indisreplident;
2348 : :
2349 : : /* Copy xmin too, as that is needed to make sense of indcheckxmin */
6051 tgl@sss.pgh.pa.us 2350 [ + + ]: 11964 : HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
2351 : : HeapTupleHeaderGetXmin(tuple->t_data));
2352 : :
6192 2353 : 11964 : ReleaseSysCache(tuple);
2354 : : }
2355 : :
2356 : : /* Okay, now it's valid again */
7169 2357 : 34681 : relation->rd_isvalid = true;
2358 : : }
2359 : :
2360 : : /*
2361 : : * RelationReloadNailed - reload minimal information for nailed relations.
2362 : : *
2363 : : * The structure of a nailed relation can never change (which is good, because
2364 : : * we rely on knowing their structure to be able to read catalog content). But
2365 : : * some parts, e.g. pg_class.relfrozenxid, are still important to have
2366 : : * accurate content for. Therefore those need to be reloaded after the arrival
2367 : : * of invalidations.
2368 : : */
2369 : : static void
2133 andres@anarazel.de 2370 : 111066 : RelationReloadNailed(Relation relation)
2371 : : {
2372 [ - + ]: 111066 : Assert(relation->rd_isnailed);
2373 : :
2374 : : /*
2375 : : * Redo RelationInitPhysicalAddr in case it is a mapped relation whose
2376 : : * mapping changed.
2377 : : */
2378 : 111066 : RelationInitPhysicalAddr(relation);
2379 : :
2380 : : /* flag as needing to be revalidated */
2381 : 111066 : relation->rd_isvalid = false;
2382 : :
2383 : : /*
2384 : : * Can only reread catalog contents if in a transaction. If the relation
2385 : : * is currently open (not counting the nailed refcount), do so
2386 : : * immediately. Otherwise we've already marked the entry as possibly
2387 : : * invalid, and it'll be fixed when next opened.
2388 : : */
2389 [ + + + + ]: 111066 : if (!IsTransactionState() || relation->rd_refcnt <= 1)
2390 : 51627 : return;
2391 : :
2392 [ + + ]: 59439 : if (relation->rd_rel->relkind == RELKIND_INDEX)
2393 : : {
2394 : : /*
2395 : : * If it's a nailed-but-not-mapped index, then we need to re-read the
2396 : : * pg_class row to see if its relfilenumber changed.
2397 : : */
2398 : 290 : RelationReloadIndexInfo(relation);
2399 : : }
2400 : : else
2401 : : {
2402 : : /*
2403 : : * Reload a non-index entry. We can't easily do so if relcaches
2404 : : * aren't yet built, but that's fine because at that stage the
2405 : : * attributes that need to be current (like relfrozenxid) aren't yet
2406 : : * accessed. To ensure the entry will later be revalidated, we leave
2407 : : * it in invalid state, but allow use (cf. RelationIdGetRelation()).
2408 : : */
2409 [ + + ]: 59149 : if (criticalRelcachesBuilt)
2410 : : {
2411 : : HeapTuple pg_class_tuple;
2412 : : Form_pg_class relp;
2413 : :
2414 : : /*
2415 : : * NB: Mark the entry as valid before starting to scan, to avoid
2416 : : * self-recursion when re-building pg_class.
2417 : : */
2418 : 10866 : relation->rd_isvalid = true;
2419 : :
2420 : 10866 : pg_class_tuple = ScanPgRelation(RelationGetRelid(relation),
2421 : : true, false);
2422 : 10863 : relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
2423 : 10863 : memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE);
2424 : 10863 : heap_freetuple(pg_class_tuple);
2425 : :
2426 : : /*
2427 : : * Again mark as valid, to protect against concurrently arriving
2428 : : * invalidations.
2429 : : */
2430 : 10863 : relation->rd_isvalid = true;
2431 : : }
2432 : : }
2433 : : }
2434 : :
2435 : : /*
2436 : : * RelationDestroyRelation
2437 : : *
2438 : : * Physically delete a relation cache entry and all subsidiary data.
2439 : : * Caller must already have unhooked the entry from the hash table.
2440 : : */
2441 : : static void
3661 simon@2ndQuadrant.co 2442 : 505423 : RelationDestroyRelation(Relation relation, bool remember_tupdesc)
2443 : : {
5206 tgl@sss.pgh.pa.us 2444 [ - + ]: 505423 : Assert(RelationHasReferenceCountZero(relation));
2445 : :
2446 : : /*
2447 : : * Make sure smgr and lower levels close the relation's files, if they
2448 : : * weren't closed already. (This was probably done by caller, but let's
2449 : : * just be real sure.)
2450 : : */
2451 : 505423 : RelationCloseSmgr(relation);
2452 : :
2453 : : /* break mutual link with stats entry */
739 andres@anarazel.de 2454 : 505423 : pgstat_unlink_relation(relation);
2455 : :
2456 : : /*
2457 : : * Free all the subsidiary data structures of the relcache entry, then the
2458 : : * entry itself.
2459 : : */
5206 tgl@sss.pgh.pa.us 2460 [ + - ]: 505423 : if (relation->rd_rel)
2461 : 505423 : pfree(relation->rd_rel);
2462 : : /* can't use DecrTupleDescRefCount here */
2463 [ - + ]: 505423 : Assert(relation->rd_att->tdrefcount > 0);
2464 [ + + ]: 505423 : if (--relation->rd_att->tdrefcount == 0)
2465 : : {
2466 : : /*
2467 : : * If we Rebuilt a relcache entry during a transaction then its
2468 : : * possible we did that because the TupDesc changed as the result of
2469 : : * an ALTER TABLE that ran at less than AccessExclusiveLock. It's
2470 : : * possible someone copied that TupDesc, in which case the copy would
2471 : : * point to free'd memory. So if we rebuild an entry we keep the
2472 : : * TupDesc around until end of transaction, to be safe.
2473 : : */
3661 simon@2ndQuadrant.co 2474 [ + + ]: 503997 : if (remember_tupdesc)
2475 : 9405 : RememberToFreeTupleDescAtEOX(relation->rd_att);
2476 : : else
2477 : 494592 : FreeTupleDesc(relation->rd_att);
2478 : : }
2857 tgl@sss.pgh.pa.us 2479 : 505423 : FreeTriggerDesc(relation->trigdesc);
2480 : 505423 : list_free_deep(relation->rd_fkeylist);
5206 2481 : 505423 : list_free(relation->rd_indexlist);
1123 2482 : 505423 : list_free(relation->rd_statlist);
3623 2483 : 505423 : bms_free(relation->rd_keyattr);
2642 peter_e@gmx.net 2484 : 505423 : bms_free(relation->rd_pkattr);
3623 tgl@sss.pgh.pa.us 2485 : 505423 : bms_free(relation->rd_idattr);
391 tomas.vondra@postgre 2486 : 505423 : bms_free(relation->rd_hotblockingattr);
2487 : 505423 : bms_free(relation->rd_summarizedattr);
782 akapila@postgresql.o 2488 [ + + ]: 505423 : if (relation->rd_pubdesc)
2489 : 3167 : pfree(relation->rd_pubdesc);
5206 tgl@sss.pgh.pa.us 2490 [ + + ]: 505423 : if (relation->rd_options)
2491 : 4189 : pfree(relation->rd_options);
2492 [ + + ]: 505423 : if (relation->rd_indextuple)
2493 : 155172 : pfree(relation->rd_indextuple);
3 akorotkov@postgresql 2494 [ - + ]: 505423 : if (relation->rd_amcache)
3 akorotkov@postgresql 2495 :UBC 0 : pfree(relation->rd_amcache);
1720 heikki.linnakangas@i 2496 [ + + ]:CBC 505423 : if (relation->rd_fdwroutine)
2497 : 132 : pfree(relation->rd_fdwroutine);
5206 tgl@sss.pgh.pa.us 2498 [ + + ]: 505423 : if (relation->rd_indexcxt)
2499 : 155172 : MemoryContextDelete(relation->rd_indexcxt);
2500 [ + + ]: 505423 : if (relation->rd_rulescxt)
2501 : 10422 : MemoryContextDelete(relation->rd_rulescxt);
3439 sfrost@snowman.net 2502 [ + + ]: 505423 : if (relation->rd_rsdesc)
2503 : 880 : MemoryContextDelete(relation->rd_rsdesc->rscxt);
2685 rhaas@postgresql.org 2504 [ + + ]: 505423 : if (relation->rd_partkeycxt)
2505 : 8004 : MemoryContextDelete(relation->rd_partkeycxt);
2506 [ + + ]: 505423 : if (relation->rd_pdcxt)
2507 : 7748 : MemoryContextDelete(relation->rd_pdcxt);
1082 alvherre@alvh.no-ip. 2508 [ + + ]: 505423 : if (relation->rd_pddcxt)
2509 : 30 : MemoryContextDelete(relation->rd_pddcxt);
1828 tgl@sss.pgh.pa.us 2510 [ + + ]: 505423 : if (relation->rd_partcheckcxt)
2511 : 1445 : MemoryContextDelete(relation->rd_partcheckcxt);
5206 2512 : 505423 : pfree(relation);
2513 : 505423 : }
2514 : :
2515 : : /*
2516 : : * RelationClearRelation
2517 : : *
2518 : : * Physically blow away a relation cache entry, or reset it and rebuild
2519 : : * it from scratch (that is, from catalog entries). The latter path is
2520 : : * used when we are notified of a change to an open relation (one with
2521 : : * refcount > 0).
2522 : : *
2523 : : * NB: when rebuilding, we'd better hold some lock on the relation,
2524 : : * else the catalog data we need to read could be changing under us.
2525 : : * Also, a rel to be rebuilt had better have refcnt > 0. This is because
2526 : : * a sinval reset could happen while we're accessing the catalogs, and
2527 : : * the rel would get blown away underneath us by RelationCacheInvalidate
2528 : : * if it has zero refcnt.
2529 : : *
2530 : : * The "rebuild" parameter is redundant in current usage because it has
2531 : : * to match the relation's refcnt status, but we keep it as a crosscheck
2532 : : * that we're doing what the caller expects.
2533 : : */
2534 : : static void
7944 bruce@momjian.us 2535 : 640859 : RelationClearRelation(Relation relation, bool rebuild)
2536 : : {
2537 : : /*
2538 : : * As per notes above, a rel to be rebuilt MUST have refcnt > 0; while of
2539 : : * course it would be an equally bad idea to blow away one with nonzero
2540 : : * refcnt, since that would leave someone somewhere with a dangling
2541 : : * pointer. All callers are expected to have verified that this holds.
2542 : : */
5114 tgl@sss.pgh.pa.us 2543 [ + + - + ]: 640859 : Assert(rebuild ?
2544 : : !RelationHasReferenceCountZero(relation) :
2545 : : RelationHasReferenceCountZero(relation));
2546 : :
2547 : : /*
2548 : : * Make sure smgr and lower levels close the relation's files, if they
2549 : : * weren't closed already. If the relation is not getting deleted, the
2550 : : * next smgr access should reopen the files automatically. This ensures
2551 : : * that the low-level file access state is updated after, say, a vacuum
2552 : : * truncation.
2553 : : */
7034 2554 : 640859 : RelationCloseSmgr(relation);
2555 : :
2556 : : /* Free AM cached data, if any */
3 akorotkov@postgresql 2557 [ + + ]: 640859 : if (relation->rd_amcache)
2558 : 37826 : pfree(relation->rd_amcache);
2559 : 640859 : relation->rd_amcache = NULL;
2560 : :
2561 : : /*
2562 : : * Treat nailed-in system relations separately, they always need to be
2563 : : * accessible, so we can't blow them away.
2564 : : */
8960 tgl@sss.pgh.pa.us 2565 [ + + ]: 640859 : if (relation->rd_isnailed)
2566 : : {
2133 andres@anarazel.de 2567 : 111066 : RelationReloadNailed(relation);
9716 bruce@momjian.us 2568 : 111063 : return;
2569 : : }
2570 : :
2571 : : /* Mark it invalid until we've finished rebuild */
1471 noah@leadboat.com 2572 : 529793 : relation->rd_isvalid = false;
2573 : :
2574 : : /* See RelationForgetRelation(). */
2575 [ + + ]: 529793 : if (relation->rd_droppedSubid != InvalidSubTransactionId)
2576 : 848 : return;
2577 : :
2578 : : /*
2579 : : * Even non-system indexes should not be blown away if they are open and
2580 : : * have valid index support information. This avoids problems with active
2581 : : * use of the index support information. As with nailed indexes, we
2582 : : * re-read the pg_class row to handle possible physical relocation of the
2583 : : * index, and we check for pg_index updates too.
2584 : : */
2277 alvherre@alvh.no-ip. 2585 [ + + ]: 528945 : if ((relation->rd_rel->relkind == RELKIND_INDEX ||
2586 [ + + ]: 340616 : relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) &&
6660 tgl@sss.pgh.pa.us 2587 [ + + ]: 193804 : relation->rd_refcnt > 0 &&
2588 [ + + ]: 38634 : relation->rd_indexcxt != NULL)
2589 : : {
3720 2590 [ + - ]: 23512 : if (IsTransactionState())
2591 : 23512 : RelationReloadIndexInfo(relation);
6660 2592 : 23512 : return;
2593 : : }
2594 : :
2595 : : /*
2596 : : * If we're really done with the relcache entry, blow it away. But if
2597 : : * someone is still using it, reconstruct the whole deal without moving
2598 : : * the physical RelationData record (so that the someone's pointer is
2599 : : * still valid).
2600 : : */
7944 bruce@momjian.us 2601 [ + + ]: 505433 : if (!rebuild)
2602 : : {
2603 : : /* Remove it from the hash table */
5206 tgl@sss.pgh.pa.us 2604 [ - + - - ]: 363819 : RelationCacheDelete(relation);
2605 : :
2606 : : /* And release storage */
3661 simon@2ndQuadrant.co 2607 : 363819 : RelationDestroyRelation(relation, false);
2608 : : }
3720 tgl@sss.pgh.pa.us 2609 [ + + ]: 141614 : else if (!IsTransactionState())
2610 : : {
2611 : : /*
2612 : : * If we're not inside a valid transaction, we can't do any catalog
2613 : : * access so it's not possible to rebuild yet. Just exit, leaving
2614 : : * rd_isvalid = false so that the rebuild will occur when the entry is
2615 : : * next opened.
2616 : : *
2617 : : * Note: it's possible that we come here during subtransaction abort,
2618 : : * and the reason for wanting to rebuild is that the rel is open in
2619 : : * the outer transaction. In that case it might seem unsafe to not
2620 : : * rebuild immediately, since whatever code has the rel already open
2621 : : * will keep on using the relcache entry as-is. However, in such a
2622 : : * case the outer transaction should be holding a lock that's
2623 : : * sufficient to prevent any significant change in the rel's schema,
2624 : : * so the existing entry contents should be good enough for its
2625 : : * purposes; at worst we might be behind on statistics updates or the
2626 : : * like. (See also CheckTableNotInUse() and its callers.) These same
2627 : : * remarks also apply to the cases above where we exit without having
2628 : : * done RelationReloadIndexInfo() yet.
2629 : : */
2630 : 28 : return;
2631 : : }
2632 : : else
2633 : : {
2634 : : /*
2635 : : * Our strategy for rebuilding an open relcache entry is to build a
2636 : : * new entry from scratch, swap its contents with the old entry, and
2637 : : * finally delete the new entry (along with any infrastructure swapped
2638 : : * over from the old entry). This is to avoid trouble in case an
2639 : : * error causes us to lose control partway through. The old entry
2640 : : * will still be marked !rd_isvalid, so we'll try to rebuild it again
2641 : : * on next access. Meanwhile it's not any less valid than it was
2642 : : * before, so any code that might expect to continue accessing it
2643 : : * isn't hurt by the rebuild failure. (Consider for example a
2644 : : * subtransaction that ALTERs a table and then gets canceled partway
2645 : : * through the cache entry rebuild. The outer transaction should
2646 : : * still see the not-modified cache entry as valid.) The worst
2647 : : * consequence of an error is leaking the necessarily-unreferenced new
2648 : : * entry, and this shouldn't happen often enough for that to be a big
2649 : : * problem.
2650 : : *
2651 : : * When rebuilding an open relcache entry, we must preserve ref count,
2652 : : * rd_*Subid, and rd_toastoid state. Also attempt to preserve the
2653 : : * pg_class entry (rd_rel), tupledesc, rewrite-rule, partition key,
2654 : : * and partition descriptor substructures in place, because various
2655 : : * places assume that these structures won't move while they are
2656 : : * working with an open relcache entry. (Note: the refcount
2657 : : * mechanism for tupledescs might someday allow us to remove this hack
2658 : : * for the tupledesc.)
2659 : : *
2660 : : * Note that this process does not touch CurrentResourceOwner; which
2661 : : * is good because whatever ref counts the entry may have do not
2662 : : * necessarily belong to that resource owner.
2663 : : */
2664 : : Relation newrel;
6940 2665 : 141586 : Oid save_relid = RelationGetRelid(relation);
2666 : : bool keep_tupdesc;
2667 : : bool keep_rules;
2668 : : bool keep_policies;
2669 : : bool keep_partkey;
2670 : :
2671 : : /* Build temporary entry, but don't link it into hashtable */
5206 2672 : 141586 : newrel = RelationBuildDesc(save_relid, false);
2673 : :
2674 : : /*
2675 : : * Between here and the end of the swap, don't add code that does or
2676 : : * reasonably could read system catalogs. That range must be free
2677 : : * from invalidation processing. See RelationBuildDesc() manipulation
2678 : : * of in_progress_list.
2679 : : */
2680 : :
2681 [ - + ]: 141583 : if (newrel == NULL)
2682 : : {
2683 : : /*
2684 : : * We can validly get here, if we're using a historic snapshot in
2685 : : * which a relation, accessed from outside logical decoding, is
2686 : : * still invisible. In that case it's fine to just mark the
2687 : : * relation as invalid and return - it'll fully get reloaded by
2688 : : * the cache reset at the end of logical decoding (or at the next
2689 : : * access). During normal processing we don't want to ignore this
2690 : : * case as it shouldn't happen there, as explained below.
2691 : : */
3385 andres@anarazel.de 2692 [ # # ]:UBC 0 : if (HistoricSnapshotActive())
2693 : 0 : return;
2694 : :
2695 : : /*
2696 : : * This shouldn't happen as dropping a relation is intended to be
2697 : : * impossible if still referenced (cf. CheckTableNotInUse()). But
2698 : : * if we get here anyway, we can't just delete the relcache entry,
2699 : : * as it possibly could get accessed later (as e.g. the error
2700 : : * might get trapped and handled via a subtransaction rollback).
2701 : : */
6940 tgl@sss.pgh.pa.us 2702 [ # # ]: 0 : elog(ERROR, "relation %u deleted while still in use", save_relid);
2703 : : }
2704 : :
2705 : : /*
2706 : : * If we were to, again, have cases of the relkind of a relcache entry
2707 : : * changing, we would need to ensure that pgstats does not get
2708 : : * confused.
2709 : : */
499 andres@anarazel.de 2710 [ - + ]:CBC 141583 : Assert(relation->rd_rel->relkind == newrel->rd_rel->relkind);
2711 : :
5206 tgl@sss.pgh.pa.us 2712 : 141583 : keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
2713 : 141583 : keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
3439 sfrost@snowman.net 2714 : 141583 : keep_policies = equalRSDesc(relation->rd_rsdesc, newrel->rd_rsdesc);
2715 : : /* partkey is immutable once set up, so we can always keep it */
2685 rhaas@postgresql.org 2716 : 141583 : keep_partkey = (relation->rd_partkey != NULL);
2717 : :
2718 : : /*
2719 : : * Perform swapping of the relcache entry contents. Within this
2720 : : * process the old entry is momentarily invalid, so there *must* be no
2721 : : * possibility of CHECK_FOR_INTERRUPTS within this sequence. Do it in
2722 : : * all-in-line code for safety.
2723 : : *
2724 : : * Since the vast majority of fields should be swapped, our method is
2725 : : * to swap the whole structures and then re-swap those few fields we
2726 : : * didn't want swapped.
2727 : : */
2728 : : #define SWAPFIELD(fldtype, fldname) \
2729 : : do { \
2730 : : fldtype _tmp = newrel->fldname; \
2731 : : newrel->fldname = relation->fldname; \
2732 : : relation->fldname = _tmp; \
2733 : : } while (0)
2734 : :
2735 : : /* swap all Relation struct fields */
2736 : : {
2737 : : RelationData tmpstruct;
2738 : :
5206 tgl@sss.pgh.pa.us 2739 : 141583 : memcpy(&tmpstruct, newrel, sizeof(RelationData));
2740 : 141583 : memcpy(newrel, relation, sizeof(RelationData));
2741 : 141583 : memcpy(relation, &tmpstruct, sizeof(RelationData));
2742 : : }
2743 : :
2744 : : /* rd_smgr must not be swapped, due to back-links from smgr level */
2745 : 141583 : SWAPFIELD(SMgrRelation, rd_smgr);
2746 : : /* rd_refcnt must be preserved */
2747 : 141583 : SWAPFIELD(int, rd_refcnt);
2748 : : /* isnailed shouldn't change */
2749 [ - + ]: 141583 : Assert(newrel->rd_isnailed == relation->rd_isnailed);
2750 : : /* creation sub-XIDs must be preserved */
2751 : 141583 : SWAPFIELD(SubTransactionId, rd_createSubid);
648 rhaas@postgresql.org 2752 : 141583 : SWAPFIELD(SubTransactionId, rd_newRelfilelocatorSubid);
2753 : 141583 : SWAPFIELD(SubTransactionId, rd_firstRelfilelocatorSubid);
1471 noah@leadboat.com 2754 : 141583 : SWAPFIELD(SubTransactionId, rd_droppedSubid);
2755 : : /* un-swap rd_rel pointers, swap contents instead */
5206 tgl@sss.pgh.pa.us 2756 : 141583 : SWAPFIELD(Form_pg_class, rd_rel);
2757 : : /* ... but actually, we don't have to update newrel->rd_rel */
2758 : 141583 : memcpy(relation->rd_rel, newrel->rd_rel, CLASS_TUPLE_SIZE);
2759 : : /* preserve old tupledesc, rules, policies if no logical change */
2760 [ + + ]: 141583 : if (keep_tupdesc)
2761 : 132070 : SWAPFIELD(TupleDesc, rd_att);
2762 [ + + ]: 141583 : if (keep_rules)
2763 : : {
2764 : 134635 : SWAPFIELD(RuleLock *, rd_rules);
2765 : 134635 : SWAPFIELD(MemoryContext, rd_rulescxt);
2766 : : }
3490 sfrost@snowman.net 2767 [ + + ]: 141583 : if (keep_policies)
3439 2768 : 141439 : SWAPFIELD(RowSecurityDesc *, rd_rsdesc);
2769 : : /* toast OID override must be preserved */
5183 tgl@sss.pgh.pa.us 2770 : 141583 : SWAPFIELD(Oid, rd_toastoid);
2771 : : /* pgstat_info / enabled must be preserved */
5206 2772 : 141583 : SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
739 andres@anarazel.de 2773 : 141583 : SWAPFIELD(bool, pgstat_enabled);
2774 : : /* preserve old partition key if we have one */
2685 rhaas@postgresql.org 2775 [ + + ]: 141583 : if (keep_partkey)
2776 : : {
2777 : 6300 : SWAPFIELD(PartitionKey, rd_partkey);
2778 : 6300 : SWAPFIELD(MemoryContext, rd_partkeycxt);
2779 : : }
1082 alvherre@alvh.no-ip. 2780 [ + + - + ]: 141583 : if (newrel->rd_pdcxt != NULL || newrel->rd_pddcxt != NULL)
2781 : : {
2782 : : /*
2783 : : * We are rebuilding a partitioned relation with a non-zero
2784 : : * reference count, so we must keep the old partition descriptor
2785 : : * around, in case there's a PartitionDirectory with a pointer to
2786 : : * it. This means we can't free the old rd_pdcxt yet. (This is
2787 : : * necessary because RelationGetPartitionDesc hands out direct
2788 : : * pointers to the relcache's data structure, unlike our usual
2789 : : * practice which is to hand out copies. We'd have the same
2790 : : * problem with rd_partkey, except that we always preserve that
2791 : : * once created.)
2792 : : *
2793 : : * To ensure that it's not leaked completely, re-attach it to the
2794 : : * new reldesc, or make it a child of the new reldesc's rd_pdcxt
2795 : : * in the unlikely event that there is one already. (Compare hack
2796 : : * in RelationBuildPartitionDesc.) RelationClose will clean up
2797 : : * any such contexts once the reference count reaches zero.
2798 : : *
2799 : : * In the case where the reference count is zero, this code is not
2800 : : * reached, which should be OK because in that case there should
2801 : : * be no PartitionDirectory with a pointer to the old entry.
2802 : : *
2803 : : * Note that newrel and relation have already been swapped, so the
2804 : : * "old" partition descriptor is actually the one hanging off of
2805 : : * newrel.
2806 : : */
1572 tgl@sss.pgh.pa.us 2807 : 5139 : relation->rd_partdesc = NULL; /* ensure rd_partdesc is invalid */
1082 alvherre@alvh.no-ip. 2808 : 5139 : relation->rd_partdesc_nodetached = NULL;
2809 : 5139 : relation->rd_partdesc_nodetached_xmin = InvalidTransactionId;
1572 tgl@sss.pgh.pa.us 2810 [ - + ]: 5139 : if (relation->rd_pdcxt != NULL) /* probably never happens */
1572 tgl@sss.pgh.pa.us 2811 :UBC 0 : MemoryContextSetParent(newrel->rd_pdcxt, relation->rd_pdcxt);
2812 : : else
1572 tgl@sss.pgh.pa.us 2813 :CBC 5139 : relation->rd_pdcxt = newrel->rd_pdcxt;
1082 alvherre@alvh.no-ip. 2814 [ - + ]: 5139 : if (relation->rd_pddcxt != NULL)
1082 alvherre@alvh.no-ip. 2815 :UBC 0 : MemoryContextSetParent(newrel->rd_pddcxt, relation->rd_pddcxt);
2816 : : else
1082 alvherre@alvh.no-ip. 2817 :CBC 5139 : relation->rd_pddcxt = newrel->rd_pddcxt;
2818 : : /* drop newrel's pointers so we don't destroy it below */
1865 rhaas@postgresql.org 2819 : 5139 : newrel->rd_partdesc = NULL;
1082 alvherre@alvh.no-ip. 2820 : 5139 : newrel->rd_partdesc_nodetached = NULL;
2821 : 5139 : newrel->rd_partdesc_nodetached_xmin = InvalidTransactionId;
1865 rhaas@postgresql.org 2822 : 5139 : newrel->rd_pdcxt = NULL;
1082 alvherre@alvh.no-ip. 2823 : 5139 : newrel->rd_pddcxt = NULL;
2824 : : }
2825 : :
2826 : : #undef SWAPFIELD
2827 : :
2828 : : /* And now we can throw away the temporary entry */
3661 simon@2ndQuadrant.co 2829 : 141583 : RelationDestroyRelation(newrel, !keep_tupdesc);
2830 : : }
2831 : : }
2832 : :
2833 : : /*
2834 : : * RelationFlushRelation
2835 : : *
2836 : : * Rebuild the relation if it is open (refcount > 0), else blow it away.
2837 : : * This is used when we receive a cache invalidation event for the rel.
2838 : : */
2839 : : static void
8503 tgl@sss.pgh.pa.us 2840 : 318947 : RelationFlushRelation(Relation relation)
2841 : : {
6289 bruce@momjian.us 2842 [ + + ]: 318947 : if (relation->rd_createSubid != InvalidSubTransactionId ||
648 rhaas@postgresql.org 2843 [ + + ]: 194524 : relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId)
2844 : : {
2845 : : /*
2846 : : * New relcache entries are always rebuilt, not flushed; else we'd
2847 : : * forget the "new" status of the relation. Ditto for the
2848 : : * new-relfilenumber status.
2849 : : *
2850 : : * The rel could have zero refcnt here, so temporarily increment the
2851 : : * refcnt to ensure it's safe to rebuild it. We can assume that the
2852 : : * current transaction has some lock on the rel already.
2853 : : */
5114 tgl@sss.pgh.pa.us 2854 : 130944 : RelationIncrementReferenceCount(relation);
2855 : 130944 : RelationClearRelation(relation, true);
2856 : 130941 : RelationDecrementReferenceCount(relation);
2857 : : }
2858 : : else
2859 : : {
2860 : : /*
2861 : : * Pre-existing rels can be dropped from the relcache if not open.
2862 : : */
5031 bruce@momjian.us 2863 : 188003 : bool rebuild = !RelationHasReferenceCountZero(relation);
2864 : :
5114 tgl@sss.pgh.pa.us 2865 : 188003 : RelationClearRelation(relation, rebuild);
2866 : : }
8960 2867 : 318944 : }
2868 : :
2869 : : /*
2870 : : * RelationForgetRelation - caller reports that it dropped the relation
2871 : : */
2872 : : void
9716 bruce@momjian.us 2873 : 32415 : RelationForgetRelation(Oid rid)
2874 : : {
2875 : : Relation relation;
2876 : :
2877 [ + - ]: 32415 : RelationIdCacheLookup(rid, relation);
2878 : :
7998 tgl@sss.pgh.pa.us 2879 [ - + ]: 32415 : if (!PointerIsValid(relation))
7998 tgl@sss.pgh.pa.us 2880 :UBC 0 : return; /* not in cache, nothing to do */
2881 : :
7998 tgl@sss.pgh.pa.us 2882 [ - + ]:CBC 32415 : if (!RelationHasReferenceCountZero(relation))
7569 tgl@sss.pgh.pa.us 2883 [ # # ]:UBC 0 : elog(ERROR, "relation %u is still open", rid);
2884 : :
1471 noah@leadboat.com 2885 [ - + ]:CBC 32415 : Assert(relation->rd_droppedSubid == InvalidSubTransactionId);
2886 [ + + ]: 32415 : if (relation->rd_createSubid != InvalidSubTransactionId ||
648 rhaas@postgresql.org 2887 [ + + ]: 32004 : relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId)
2888 : : {
2889 : : /*
2890 : : * In the event of subtransaction rollback, we must not forget
2891 : : * rd_*Subid. Mark the entry "dropped" so RelationClearRelation()
2892 : : * invalidates it in lieu of destroying it. (If we're in a top
2893 : : * transaction, we could opt to destroy the entry.)
2894 : : */
1471 noah@leadboat.com 2895 : 424 : relation->rd_droppedSubid = GetCurrentSubTransactionId();
2896 : : }
2897 : :
7998 tgl@sss.pgh.pa.us 2898 : 32415 : RelationClearRelation(relation, false);
2899 : : }
2900 : :
2901 : : /*
2902 : : * RelationCacheInvalidateEntry
2903 : : *
2904 : : * This routine is invoked for SI cache flush messages.
2905 : : *
2906 : : * Any relcache entry matching the relid must be flushed. (Note: caller has
2907 : : * already determined that the relid belongs to our database or is a shared
2908 : : * relation.)
2909 : : *
2910 : : * We used to skip local relations, on the grounds that they could
2911 : : * not be targets of cross-backend SI update messages; but it seems
2912 : : * safer to process them, so that our *own* SI update messages will
2913 : : * have the same effects during CommandCounterIncrement for both
2914 : : * local and nonlocal relations.
2915 : : */
2916 : : void
7034 2917 : 1209768 : RelationCacheInvalidateEntry(Oid relationId)
2918 : : {
2919 : : Relation relation;
2920 : :
9716 bruce@momjian.us 2921 [ + + ]: 1209768 : RelationIdCacheLookup(relationId, relation);
2922 : :
8840 tgl@sss.pgh.pa.us 2923 [ + + ]: 1209768 : if (PointerIsValid(relation))
2924 : : {
8090 2925 : 318947 : relcacheInvalsReceived++;
8503 2926 : 318947 : RelationFlushRelation(relation);
2927 : : }
2928 : : else
2929 : : {
2930 : : int i;
2931 : :
904 noah@leadboat.com 2932 [ + + ]: 925463 : for (i = 0; i < in_progress_list_len; i++)
2933 [ + + ]: 34642 : if (in_progress_list[i].reloid == relationId)
2934 : 22 : in_progress_list[i].invalidated = true;
2935 : : }
10141 scrappy@hub.org 2936 : 1209765 : }
2937 : :
2938 : : /*
2939 : : * RelationCacheInvalidate
2940 : : * Blow away cached relation descriptors that have zero reference counts,
2941 : : * and rebuild those with positive reference counts. Also reset the smgr
2942 : : * relation cache and re-read relation mapping data.
2943 : : *
2944 : : * Apart from debug_discard_caches, this is currently used only to recover
2945 : : * from SI message buffer overflow, so we do not touch relations having
2946 : : * new-in-transaction relfilenumbers; they cannot be targets of cross-backend
2947 : : * SI updates (and our own updates now go through a separate linked list
2948 : : * that isn't limited by the SI message buffer size).
2949 : : *
2950 : : * We do this in two phases: the first pass deletes deletable items, and
2951 : : * the second one rebuilds the rebuildable items. This is essential for
2952 : : * safety, because hash_seq_search only copes with concurrent deletion of
2953 : : * the element it is currently visiting. If a second SI overflow were to
2954 : : * occur while we are walking the table, resulting in recursive entry to
2955 : : * this routine, we could crash because the inner invocation blows away
2956 : : * the entry next to be visited by the outer scan. But this way is OK,
2957 : : * because (a) during the first pass we won't process any more SI messages,
2958 : : * so hash_seq_search will complete safely; (b) during the second pass we
2959 : : * only hold onto pointers to nondeletable entries.
2960 : : *
2961 : : * The two-phase approach also makes it easy to update relfilenumbers for
2962 : : * mapped relations before we do anything else, and to ensure that the
2963 : : * second pass processes nailed-in-cache items before other nondeletable
2964 : : * items. This should ensure that system catalogs are up to date before
2965 : : * we attempt to use them to reload information about other open relations.
2966 : : *
2967 : : * After those two phases of work having immediate effects, we normally
2968 : : * signal any RelationBuildDesc() on the stack to start over. However, we
2969 : : * don't do this if called as part of debug_discard_caches. Otherwise,
2970 : : * RelationBuildDesc() would become an infinite loop.
2971 : : */
2972 : : void
904 noah@leadboat.com 2973 : 2068 : RelationCacheInvalidate(bool debug_discard)
2974 : : {
2975 : : HASH_SEQ_STATUS status;
2976 : : RelIdCacheEnt *idhentry;
2977 : : Relation relation;
7508 tgl@sss.pgh.pa.us 2978 : 2068 : List *rebuildFirstList = NIL;
8424 bruce@momjian.us 2979 : 2068 : List *rebuildList = NIL;
2980 : : ListCell *l;
2981 : : int i;
2982 : :
2983 : : /*
2984 : : * Reload relation mapping data before starting to reconstruct cache.
2985 : : */
4625 tgl@sss.pgh.pa.us 2986 : 2068 : RelationMapInvalidateAll();
2987 : :
2988 : : /* Phase 1 */
8055 2989 : 2068 : hash_seq_init(&status, RelationIdCache);
2990 : :
2991 [ + + ]: 231931 : while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
2992 : : {
2993 : 229863 : relation = idhentry->reldesc;
2994 : :
2995 : : /*
2996 : : * Ignore new relations; no other backend will manipulate them before
2997 : : * we commit. Likewise, before replacing a relation's relfilelocator,
2998 : : * we shall have acquired AccessExclusiveLock and drained any
2999 : : * applicable pending invalidations.
3000 : : */
4129 simon@2ndQuadrant.co 3001 [ + + ]: 229863 : if (relation->rd_createSubid != InvalidSubTransactionId ||
648 rhaas@postgresql.org 3002 [ + + ]: 229854 : relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId)
8227 tgl@sss.pgh.pa.us 3003 : 11 : continue;
3004 : :
8090 3005 : 229852 : relcacheInvalsReceived++;
3006 : :
7917 3007 [ + + ]: 229852 : if (RelationHasReferenceCountZero(relation))
3008 : : {
3009 : : /* Delete this entry immediately */
7508 3010 [ - + ]: 184959 : Assert(!relation->rd_isnailed);
8227 3011 : 184959 : RelationClearRelation(relation, false);
3012 : : }
3013 : : else
3014 : : {
3015 : : /*
3016 : : * If it's a mapped relation, immediately update its rd_locator in
3017 : : * case its relfilenumber changed. We must do this during phase 1
3018 : : * in case the relation is consulted during rebuild of other
3019 : : * relcache entries in phase 2. It's safe since consulting the
3020 : : * map doesn't involve any access to relcache entries.
3021 : : */
4625 3022 [ + + - + : 44893 : if (RelationIsMapped(relation))
- - - - -
- + + ]
3023 : 34712 : RelationInitPhysicalAddr(relation);
3024 : :
3025 : : /*
3026 : : * Add this entry to list of stuff to rebuild in second pass.
3027 : : * pg_class goes to the front of rebuildFirstList while
3028 : : * pg_class_oid_index goes to the back of rebuildFirstList, so
3029 : : * they are done first and second respectively. Other nailed
3030 : : * relations go to the front of rebuildList, so they'll be done
3031 : : * next in no particular order; and everything else goes to the
3032 : : * back of rebuildList.
3033 : : */
3034 [ + + ]: 44893 : if (RelationGetRelid(relation) == RelationRelationId)
3035 : 2030 : rebuildFirstList = lcons(relation, rebuildFirstList);
3036 [ + + ]: 42863 : else if (RelationGetRelid(relation) == ClassOidIndexId)
3037 : 2030 : rebuildFirstList = lappend(rebuildFirstList, relation);
3038 [ + + ]: 40833 : else if (relation->rd_isnailed)
7508 3039 : 40802 : rebuildList = lcons(relation, rebuildList);
3040 : : else
4625 3041 : 31 : rebuildList = lappend(rebuildList, relation);
3042 : : }
3043 : : }
3044 : :
3045 : : /*
3046 : : * We cannot destroy the SMgrRelations as there might still be references
3047 : : * to them, but close the underlying file descriptors.
3048 : : */
34 heikki.linnakangas@i 3049 :GNC 2068 : smgrreleaseall();
3050 : :
3051 : : /* Phase 2: rebuild the items found to need rebuild in phase 1 */
6660 tgl@sss.pgh.pa.us 3052 [ + + + + :CBC 6128 : foreach(l, rebuildFirstList)
+ + ]
3053 : : {
3054 : 4060 : relation = (Relation) lfirst(l);
3055 : 4060 : RelationClearRelation(relation, true);
3056 : : }
3057 : 2068 : list_free(rebuildFirstList);
8227 3058 [ + - + + : 42901 : foreach(l, rebuildList)
+ + ]
3059 : : {
3060 : 40833 : relation = (Relation) lfirst(l);
3061 : 40833 : RelationClearRelation(relation, true);
3062 : : }
7259 neilc@samurai.com 3063 : 2068 : list_free(rebuildList);
3064 : :
904 noah@leadboat.com 3065 [ + - ]: 2068 : if (!debug_discard)
3066 : : /* Any RelationBuildDesc() on the stack must start over. */
3067 [ + + ]: 2071 : for (i = 0; i < in_progress_list_len; i++)
3068 : 3 : in_progress_list[i].invalidated = true;
10141 scrappy@hub.org 3069 : 2068 : }
3070 : :
3071 : : static void
3661 simon@2ndQuadrant.co 3072 : 9405 : RememberToFreeTupleDescAtEOX(TupleDesc td)
3073 : : {
3074 [ + + ]: 9405 : if (EOXactTupleDescArray == NULL)
3075 : : {
3076 : : MemoryContext oldcxt;
3077 : :
3078 : 5215 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
3079 : :
3080 : 5215 : EOXactTupleDescArray = (TupleDesc *) palloc(16 * sizeof(TupleDesc));
3081 : 5215 : EOXactTupleDescArrayLen = 16;
3082 : 5215 : NextEOXactTupleDescNum = 0;
3083 : 5215 : MemoryContextSwitchTo(oldcxt);
3084 : : }
3085 [ + + ]: 4190 : else if (NextEOXactTupleDescNum >= EOXactTupleDescArrayLen)
3086 : : {
3631 bruce@momjian.us 3087 : 17 : int32 newlen = EOXactTupleDescArrayLen * 2;
3088 : :
3661 simon@2ndQuadrant.co 3089 [ - + ]: 17 : Assert(EOXactTupleDescArrayLen > 0);
3090 : :
3091 : 17 : EOXactTupleDescArray = (TupleDesc *) repalloc(EOXactTupleDescArray,
3092 : : newlen * sizeof(TupleDesc));
3093 : 17 : EOXactTupleDescArrayLen = newlen;
3094 : : }
3095 : :
3096 : 9405 : EOXactTupleDescArray[NextEOXactTupleDescNum++] = td;
3097 : 9405 : }
3098 : :
3099 : : #ifdef USE_ASSERT_CHECKING
3100 : : static void
1471 noah@leadboat.com 3101 : 899244 : AssertPendingSyncConsistency(Relation relation)
3102 : : {
3103 : 899244 : bool relcache_verdict =
331 tgl@sss.pgh.pa.us 3104 [ + + ]: 1791241 : RelationIsPermanent(relation) &&
3105 [ + + ]: 891997 : ((relation->rd_createSubid != InvalidSubTransactionId &&
3106 [ + + + + : 9499 : RELKIND_HAS_STORAGE(relation->rd_rel->relkind)) ||
+ + + + +
+ ]
3107 [ + + ]: 882787 : relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId);
3108 : :
648 rhaas@postgresql.org 3109 [ - + ]: 899244 : Assert(relcache_verdict == RelFileLocatorSkippingWAL(relation->rd_locator));
3110 : :
1471 noah@leadboat.com 3111 [ + + ]: 899244 : if (relation->rd_droppedSubid != InvalidSubTransactionId)
3112 [ + - + + : 37 : Assert(!relation->rd_isvalid &&
- + ]
3113 : : (relation->rd_createSubid != InvalidSubTransactionId ||
3114 : : relation->rd_firstRelfilelocatorSubid != InvalidSubTransactionId));
3115 : 899244 : }
3116 : :
3117 : : /*
3118 : : * AssertPendingSyncs_RelationCache
3119 : : *
3120 : : * Assert that relcache.c and storage.c agree on whether to skip WAL.
3121 : : */
3122 : : void
3123 : 5647 : AssertPendingSyncs_RelationCache(void)
3124 : : {
3125 : : HASH_SEQ_STATUS status;
3126 : : LOCALLOCK *locallock;
3127 : : Relation *rels;
3128 : : int maxrels;
3129 : : int nrels;
3130 : : RelIdCacheEnt *idhentry;
3131 : : int i;
3132 : :
3133 : : /*
3134 : : * Open every relation that this transaction has locked. If, for some
3135 : : * relation, storage.c is skipping WAL and relcache.c is not skipping WAL,
3136 : : * a CommandCounterIncrement() typically yields a local invalidation
3137 : : * message that destroys the relcache entry. By recreating such entries
3138 : : * here, we detect the problem.
3139 : : */
3140 : 5647 : PushActiveSnapshot(GetTransactionSnapshot());
3141 : 5647 : maxrels = 1;
3142 : 5647 : rels = palloc(maxrels * sizeof(*rels));
3143 : 5647 : nrels = 0;
3144 : 5647 : hash_seq_init(&status, GetLockMethodLocalHash());
3145 [ + + ]: 38224 : while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
3146 : : {
3147 : : Oid relid;
3148 : : Relation r;
3149 : :
3150 [ - + ]: 32577 : if (locallock->nLocks <= 0)
1471 noah@leadboat.com 3151 :UBC 0 : continue;
1471 noah@leadboat.com 3152 [ + + ]:CBC 32577 : if ((LockTagType) locallock->tag.lock.locktag_type !=
3153 : : LOCKTAG_RELATION)
3154 : 10494 : continue;
3155 : 22083 : relid = ObjectIdGetDatum(locallock->tag.lock.locktag_field2);
3156 : 22083 : r = RelationIdGetRelation(relid);
3157 [ + + ]: 22083 : if (!RelationIsValid(r))
3158 : 722 : continue;
3159 [ + + ]: 21361 : if (nrels >= maxrels)
3160 : : {
3161 : 8816 : maxrels *= 2;
3162 : 8816 : rels = repalloc(rels, maxrels * sizeof(*rels));
3163 : : }
3164 : 21361 : rels[nrels++] = r;
3165 : : }
3166 : :
3167 : 5647 : hash_seq_init(&status, RelationIdCache);
3168 [ + + ]: 904891 : while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
3169 : 899244 : AssertPendingSyncConsistency(idhentry->reldesc);
3170 : :
3171 [ + + ]: 27008 : for (i = 0; i < nrels; i++)
3172 : 21361 : RelationClose(rels[i]);
3173 : 5647 : PopActiveSnapshot();
3174 : 5647 : }
3175 : : #endif
3176 : :
3177 : : /*
3178 : : * AtEOXact_RelationCache
3179 : : *
3180 : : * Clean up the relcache at main-transaction commit or abort.
3181 : : *
3182 : : * Note: this must be called *before* processing invalidation messages.
3183 : : * In the case of abort, we don't want to try to rebuild any invalidated
3184 : : * cache entries (since we can't safely do database accesses). Therefore
3185 : : * we must reset refcnts before handling pending invalidations.
3186 : : *
3187 : : * As of PostgreSQL 8.1, relcache refcnts should get released by the
3188 : : * ResourceOwner mechanism. This routine just does a debugging
3189 : : * cross-check that no pins remain. However, we also need to do special
3190 : : * cleanup when the current transaction created any relations or made use
3191 : : * of forced index lists.
3192 : : */
3193 : : void
7227 tgl@sss.pgh.pa.us 3194 : 433081 : AtEOXact_RelationCache(bool isCommit)
3195 : : {
3196 : : HASH_SEQ_STATUS status;
3197 : : RelIdCacheEnt *idhentry;
3198 : : int i;
3199 : :
3200 : : /*
3201 : : * Forget in_progress_list. This is relevant when we're aborting due to
3202 : : * an error during RelationBuildDesc().
3203 : : */
904 noah@leadboat.com 3204 [ + + - + ]: 433081 : Assert(in_progress_list_len == 0 || !isCommit);
3205 : 433081 : in_progress_list_len = 0;
3206 : :
3207 : : /*
3208 : : * Unless the eoxact_list[] overflowed, we only need to examine the rels
3209 : : * listed in it. Otherwise fall back on a hash_seq_search scan.
3210 : : *
3211 : : * For simplicity, eoxact_list[] entries are not deleted till end of
3212 : : * top-level transaction, even though we could remove them at
3213 : : * subtransaction end in some cases, or remove relations from the list if
3214 : : * they are cleared for other reasons. Therefore we should expect the
3215 : : * case that list entries are not found in the hashtable; if not, there's
3216 : : * nothing to do for them.
3217 : : */
4102 tgl@sss.pgh.pa.us 3218 [ + + ]: 433081 : if (eoxact_list_overflowed)
3219 : : {
3220 : 44 : hash_seq_init(&status, RelationIdCache);
3221 [ + + ]: 13113 : while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
3222 : : {
3223 : 13069 : AtEOXact_cleanup(idhentry->reldesc, isCommit);
3224 : : }
3225 : : }
3226 : : else
3227 : : {
3228 [ + + ]: 486776 : for (i = 0; i < eoxact_list_len; i++)
3229 : : {
3230 : 53739 : idhentry = (RelIdCacheEnt *) hash_search(RelationIdCache,
433 peter@eisentraut.org 3231 : 53739 : &eoxact_list[i],
3232 : : HASH_FIND,
3233 : : NULL);
4102 tgl@sss.pgh.pa.us 3234 [ + + ]: 53739 : if (idhentry != NULL)
3235 : 52722 : AtEOXact_cleanup(idhentry->reldesc, isCommit);
3236 : : }
3237 : : }
3238 : :
3661 simon@2ndQuadrant.co 3239 [ + + ]: 433081 : if (EOXactTupleDescArrayLen > 0)
3240 : : {
3241 [ - + ]: 5215 : Assert(EOXactTupleDescArray != NULL);
3242 [ + + ]: 14620 : for (i = 0; i < NextEOXactTupleDescNum; i++)
3243 : 9405 : FreeTupleDesc(EOXactTupleDescArray[i]);
3244 : 5215 : pfree(EOXactTupleDescArray);
3245 : 5215 : EOXactTupleDescArray = NULL;
3246 : : }
3247 : :
3248 : : /* Now we're out of the transaction and can clear the lists */
4102 tgl@sss.pgh.pa.us 3249 : 433081 : eoxact_list_len = 0;
3250 : 433081 : eoxact_list_overflowed = false;
3661 simon@2ndQuadrant.co 3251 : 433081 : NextEOXactTupleDescNum = 0;
3252 : 433081 : EOXactTupleDescArrayLen = 0;
4102 tgl@sss.pgh.pa.us 3253 : 433081 : }
3254 : :
3255 : : /*
3256 : : * AtEOXact_cleanup
3257 : : *
3258 : : * Clean up a single rel at main-transaction commit or abort
3259 : : *
3260 : : * NB: this processing must be idempotent, because EOXactListAdd() doesn't
3261 : : * bother to prevent duplicate entries in eoxact_list[].
3262 : : */
3263 : : static void
3264 : 65791 : AtEOXact_cleanup(Relation relation, bool isCommit)
3265 : : {
1471 noah@leadboat.com 3266 : 65791 : bool clear_relcache = false;
3267 : :
3268 : : /*
3269 : : * The relcache entry's ref count should be back to its normal
3270 : : * not-in-a-transaction state: 0 unless it's nailed in cache.
3271 : : *
3272 : : * In bootstrap mode, this is NOT true, so don't check it --- the
3273 : : * bootstrap code expects relations to stay open across start/commit
3274 : : * transaction calls. (That seems bogus, but it's not worth fixing.)
3275 : : *
3276 : : * Note: ideally this check would be applied to every relcache entry, not
3277 : : * just those that have eoxact work to do. But it's not worth forcing a
3278 : : * scan of the whole relcache just for this. (Moreover, doing so would
3279 : : * mean that assert-enabled testing never tests the hash_search code path
3280 : : * above, which seems a bad idea.)
3281 : : */
3282 : : #ifdef USE_ASSERT_CHECKING
3973 bruce@momjian.us 3283 [ + + ]: 65791 : if (!IsBootstrapProcessingMode())
3284 : : {
3285 : : int expected_refcnt;
3286 : :
3287 : 55651 : expected_refcnt = relation->rd_isnailed ? 1 : 0;
3288 [ - + ]: 55651 : Assert(relation->rd_refcnt == expected_refcnt);
3289 : : }
3290 : : #endif
3291 : :
3292 : : /*
3293 : : * Is the relation live after this transaction ends?
3294 : : *
3295 : : * During commit, clear the relcache entry if it is preserved after
3296 : : * relation drop, in order not to orphan the entry. During rollback,
3297 : : * clear the relcache entry if the relation is created in the current
3298 : : * transaction since it isn't interesting any longer once we are out of
3299 : : * the transaction.
3300 : : */
1471 noah@leadboat.com 3301 : 65791 : clear_relcache =
3302 : : (isCommit ?
3303 [ + + ]: 65791 : relation->rd_droppedSubid != InvalidSubTransactionId :
3304 [ + + ]: 2018 : relation->rd_createSubid != InvalidSubTransactionId);
3305 : :
3306 : : /*
3307 : : * Since we are now out of the transaction, reset the subids to zero. That
3308 : : * also lets RelationClearRelation() drop the relcache entry.
3309 : : */
3310 : 65791 : relation->rd_createSubid = InvalidSubTransactionId;
648 rhaas@postgresql.org 3311 : 65791 : relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
3312 : 65791 : relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1471 noah@leadboat.com 3313 : 65791 : relation->rd_droppedSubid = InvalidSubTransactionId;
3314 : :
3315 [ + + ]: 65791 : if (clear_relcache)
3316 : : {
3317 [ + - ]: 2108 : if (RelationHasReferenceCountZero(relation))
3318 : : {
3973 bruce@momjian.us 3319 : 2108 : RelationClearRelation(relation, false);
3320 : 2108 : return;
3321 : : }
3322 : : else
3323 : : {
3324 : : /*
3325 : : * Hmm, somewhere there's a (leaked?) reference to the relation.
3326 : : * We daren't remove the entry for fear of dereferencing a
3327 : : * dangling pointer later. Bleat, and mark it as not belonging to
3328 : : * the current transaction. Hopefully it'll get cleaned up
3329 : : * eventually. This must be just a WARNING to avoid
3330 : : * error-during-error-recovery loops.
3331 : : */
3145 tgl@sss.pgh.pa.us 3332 [ # # ]:UBC 0 : elog(WARNING, "cannot remove relcache entry for \"%s\" because it has nonzero refcount",
3333 : : RelationGetRelationName(relation));
3334 : : }
3335 : : }
3336 : : }
3337 : :
3338 : : /*
3339 : : * AtEOSubXact_RelationCache
3340 : : *
3341 : : * Clean up the relcache at sub-transaction commit or abort.
3342 : : *
3343 : : * Note: this must be called *before* processing invalidation messages.
3344 : : */
3345 : : void
7150 tgl@sss.pgh.pa.us 3346 :CBC 9929 : AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
3347 : : SubTransactionId parentSubid)
3348 : : {
3349 : : HASH_SEQ_STATUS status;
3350 : : RelIdCacheEnt *idhentry;
3351 : : int i;
3352 : :
3353 : : /*
3354 : : * Forget in_progress_list. This is relevant when we're aborting due to
3355 : : * an error during RelationBuildDesc(). We don't commit subtransactions
3356 : : * during RelationBuildDesc().
3357 : : */
904 noah@leadboat.com 3358 [ - + - - ]: 9929 : Assert(in_progress_list_len == 0 || !isCommit);
3359 : 9929 : in_progress_list_len = 0;
3360 : :
3361 : : /*
3362 : : * Unless the eoxact_list[] overflowed, we only need to examine the rels
3363 : : * listed in it. Otherwise fall back on a hash_seq_search scan. Same
3364 : : * logic as in AtEOXact_RelationCache.
3365 : : */
4102 tgl@sss.pgh.pa.us 3366 [ - + ]: 9929 : if (eoxact_list_overflowed)
3367 : : {
4102 tgl@sss.pgh.pa.us 3368 :UBC 0 : hash_seq_init(&status, RelationIdCache);
3369 [ # # ]: 0 : while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
3370 : : {
3371 : 0 : AtEOSubXact_cleanup(idhentry->reldesc, isCommit,
3372 : : mySubid, parentSubid);
3373 : : }
3374 : : }
3375 : : else
3376 : : {
4102 tgl@sss.pgh.pa.us 3377 [ + + ]:CBC 14521 : for (i = 0; i < eoxact_list_len; i++)
3378 : : {
3379 : 4592 : idhentry = (RelIdCacheEnt *) hash_search(RelationIdCache,
433 peter@eisentraut.org 3380 : 4592 : &eoxact_list[i],
3381 : : HASH_FIND,
3382 : : NULL);
4102 tgl@sss.pgh.pa.us 3383 [ + + ]: 4592 : if (idhentry != NULL)
3384 : 4120 : AtEOSubXact_cleanup(idhentry->reldesc, isCommit,
3385 : : mySubid, parentSubid);
3386 : : }
3387 : : }
3388 : :
3389 : : /* Don't reset the list; we still need more cleanup later */
3390 : 9929 : }
3391 : :
3392 : : /*
3393 : : * AtEOSubXact_cleanup
3394 : : *
3395 : : * Clean up a single rel at subtransaction commit or abort
3396 : : *
3397 : : * NB: this processing must be idempotent, because EOXactListAdd() doesn't
3398 : : * bother to prevent duplicate entries in eoxact_list[].
3399 : : */
3400 : : static void
3401 : 4120 : AtEOSubXact_cleanup(Relation relation, bool isCommit,
3402 : : SubTransactionId mySubid, SubTransactionId parentSubid)
3403 : : {
3404 : : /*
3405 : : * Is it a relation created in the current subtransaction?
3406 : : *
3407 : : * During subcommit, mark it as belonging to the parent, instead, as long
3408 : : * as it has not been dropped. Otherwise simply delete the relcache entry.
3409 : : * --- it isn't interesting any longer.
3410 : : */
3973 bruce@momjian.us 3411 [ + + ]: 4120 : if (relation->rd_createSubid == mySubid)
3412 : : {
3413 : : /*
3414 : : * Valid rd_droppedSubid means the corresponding relation is dropped
3415 : : * but the relcache entry is preserved for at-commit pending sync. We
3416 : : * need to drop it explicitly here not to make the entry orphan.
3417 : : */
1471 noah@leadboat.com 3418 [ + + - + ]: 99 : Assert(relation->rd_droppedSubid == mySubid ||
3419 : : relation->rd_droppedSubid == InvalidSubTransactionId);
3420 [ + + + - ]: 99 : if (isCommit && relation->rd_droppedSubid == InvalidSubTransactionId)
3973 bruce@momjian.us 3421 : 37 : relation->rd_createSubid = parentSubid;
3145 tgl@sss.pgh.pa.us 3422 [ + - ]: 62 : else if (RelationHasReferenceCountZero(relation))
3423 : : {
3424 : : /* allow the entry to be removed */
1471 noah@leadboat.com 3425 : 62 : relation->rd_createSubid = InvalidSubTransactionId;
648 rhaas@postgresql.org 3426 : 62 : relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
3427 : 62 : relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1471 noah@leadboat.com 3428 : 62 : relation->rd_droppedSubid = InvalidSubTransactionId;
3973 bruce@momjian.us 3429 : 62 : RelationClearRelation(relation, false);
3430 : 62 : return;
3431 : : }
3432 : : else
3433 : : {
3434 : : /*
3435 : : * Hmm, somewhere there's a (leaked?) reference to the relation.
3436 : : * We daren't remove the entry for fear of dereferencing a
3437 : : * dangling pointer later. Bleat, and transfer it to the parent
3438 : : * subtransaction so we can try again later. This must be just a
3439 : : * WARNING to avoid error-during-error-recovery loops.
3440 : : */
3145 tgl@sss.pgh.pa.us 3441 :UBC 0 : relation->rd_createSubid = parentSubid;
3442 [ # # ]: 0 : elog(WARNING, "cannot remove relcache entry for \"%s\" because it has nonzero refcount",
3443 : : RelationGetRelationName(relation));
3444 : : }
3445 : : }
3446 : :
3447 : : /*
3448 : : * Likewise, update or drop any new-relfilenumber-in-subtransaction record
3449 : : * or drop record.
3450 : : */
648 rhaas@postgresql.org 3451 [ + + ]:CBC 4058 : if (relation->rd_newRelfilelocatorSubid == mySubid)
3452 : : {
3973 bruce@momjian.us 3453 [ + + ]: 60 : if (isCommit)
648 rhaas@postgresql.org 3454 : 32 : relation->rd_newRelfilelocatorSubid = parentSubid;
3455 : : else
3456 : 28 : relation->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
3457 : : }
3458 : :
3459 [ + + ]: 4058 : if (relation->rd_firstRelfilelocatorSubid == mySubid)
3460 : : {
1471 noah@leadboat.com 3461 [ + + ]: 42 : if (isCommit)
648 rhaas@postgresql.org 3462 : 14 : relation->rd_firstRelfilelocatorSubid = parentSubid;
3463 : : else
3464 : 28 : relation->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
3465 : : }
3466 : :
1471 noah@leadboat.com 3467 [ + + ]: 4058 : if (relation->rd_droppedSubid == mySubid)
3468 : : {
3469 [ + + ]: 10 : if (isCommit)
3470 : 1 : relation->rd_droppedSubid = parentSubid;
3471 : : else
3472 : 9 : relation->rd_droppedSubid = InvalidSubTransactionId;
3473 : : }
3474 : : }
3475 : :
3476 : :
3477 : : /*
3478 : : * RelationBuildLocalRelation
3479 : : * Build a relcache entry for an about-to-be-created relation,
3480 : : * and enter it into the relcache.
3481 : : */
3482 : : Relation
8325 tgl@sss.pgh.pa.us 3483 : 58741 : RelationBuildLocalRelation(const char *relname,
3484 : : Oid relnamespace,
3485 : : TupleDesc tupDesc,
3486 : : Oid relid,
3487 : : Oid accessmtd,
3488 : : RelFileNumber relfilenumber,
3489 : : Oid reltablespace,
3490 : : bool shared_relation,
3491 : : bool mapped_relation,
3492 : : char relpersistence,
3493 : : char relkind)
3494 : : {
3495 : : Relation rel;
3496 : : MemoryContext oldcxt;
3497 : 58741 : int natts = tupDesc->natts;
3498 : : int i;
3499 : : bool has_not_null;
3500 : : bool nailit;
3501 : :
534 peter@eisentraut.org 3502 [ - + ]: 58741 : Assert(natts >= 0);
3503 : :
3504 : : /*
3505 : : * check for creation of a rel that must be nailed in cache.
3506 : : *
3507 : : * XXX this list had better match the relations specially handled in
3508 : : * RelationCacheInitializePhase2/3.
3509 : : */
6940 tgl@sss.pgh.pa.us 3510 [ + + ]: 58741 : switch (relid)
3511 : : {
5359 3512 : 273 : case DatabaseRelationId:
3513 : : case AuthIdRelationId:
3514 : : case AuthMemRelationId:
3515 : : case RelationRelationId:
3516 : : case AttributeRelationId:
3517 : : case ProcedureRelationId:
3518 : : case TypeRelationId:
6940 3519 : 273 : nailit = true;
3520 : 273 : break;
3521 : 58468 : default:
3522 : 58468 : nailit = false;
3523 : 58468 : break;
3524 : : }
3525 : :
3526 : : /*
3527 : : * check that hardwired list of shared rels matches what's in the
3528 : : * bootstrap .bki file. If you get a failure here during initdb, you
3529 : : * probably need to fix IsSharedRelation() to match whatever you've done
3530 : : * to the set of shared relations.
3531 : : */
6467 3532 [ - + ]: 58741 : if (shared_relation != IsSharedRelation(relid))
6467 tgl@sss.pgh.pa.us 3533 [ # # ]:UBC 0 : elog(ERROR, "shared_relation flag for \"%s\" does not match IsSharedRelation(%u)",
3534 : : relname, relid);
3535 : :
3536 : : /* Shared relations had better be mapped, too */
5180 tgl@sss.pgh.pa.us 3537 [ + + - + ]:CBC 58741 : Assert(mapped_relation || !shared_relation);
3538 : :
3539 : : /*
3540 : : * switch to the cache context to create the relcache entry.
3541 : : */
8325 3542 [ - + ]: 58741 : if (!CacheMemoryContext)
8325 tgl@sss.pgh.pa.us 3543 :UBC 0 : CreateCacheMemoryContext();
3544 : :
8689 tgl@sss.pgh.pa.us 3545 :CBC 58741 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
3546 : :
3547 : : /*
3548 : : * allocate a new relation descriptor and fill in basic state fields.
3549 : : */
7823 bruce@momjian.us 3550 : 58741 : rel = (Relation) palloc0(sizeof(RelationData));
3551 : :
3552 : : /* make sure relation is marked as having no open file yet */
7369 tgl@sss.pgh.pa.us 3553 : 58741 : rel->rd_smgr = NULL;
3554 : :
3555 : : /* mark it nailed if appropriate */
6940 3556 : 58741 : rel->rd_isnailed = nailit;
3557 : :
7211 3558 : 58741 : rel->rd_refcnt = nailit ? 1 : 0;
3559 : :
3560 : : /* it's being created in this transaction */
7150 3561 : 58741 : rel->rd_createSubid = GetCurrentSubTransactionId();
648 rhaas@postgresql.org 3562 : 58741 : rel->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
3563 : 58741 : rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1471 noah@leadboat.com 3564 : 58741 : rel->rd_droppedSubid = InvalidSubTransactionId;
3565 : :
3566 : : /*
3567 : : * create a new tuple descriptor from the one passed in. We do this
3568 : : * partly to copy it into the cache context, and partly because the new
3569 : : * relation can't have any defaults or constraints yet; they have to be
3570 : : * added in later steps, because they require additions to multiple system
3571 : : * catalogs. We can copy attnotnull constraints here, however.
3572 : : */
8078 tgl@sss.pgh.pa.us 3573 : 58741 : rel->rd_att = CreateTupleDescCopy(tupDesc);
6512 3574 : 58741 : rel->rd_att->tdrefcount = 1; /* mark as refcounted */
7821 3575 : 58741 : has_not_null = false;
8078 3576 [ + + ]: 249118 : for (i = 0; i < natts; i++)
3577 : : {
2429 andres@anarazel.de 3578 : 190377 : Form_pg_attribute satt = TupleDescAttr(tupDesc, i);
3579 : 190377 : Form_pg_attribute datt = TupleDescAttr(rel->rd_att, i);
3580 : :
3581 : 190377 : datt->attidentity = satt->attidentity;
1842 peter@eisentraut.org 3582 : 190377 : datt->attgenerated = satt->attgenerated;
2429 andres@anarazel.de 3583 : 190377 : datt->attnotnull = satt->attnotnull;
3584 : 190377 : has_not_null |= satt->attnotnull;
3585 : : }
3586 : :
7821 tgl@sss.pgh.pa.us 3587 [ + + ]: 58741 : if (has_not_null)
3588 : : {
3589 : 8790 : TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
3590 : :
3591 : 8790 : constr->has_not_null = true;
3592 : 8790 : rel->rd_att->constr = constr;
3593 : : }
3594 : :
3595 : : /*
3596 : : * initialize relation tuple form (caller may add/override data later)
3597 : : */
7823 bruce@momjian.us 3598 : 58741 : rel->rd_rel = (Form_pg_class) palloc0(CLASS_TUPLE_SIZE);
3599 : :
8055 tgl@sss.pgh.pa.us 3600 : 58741 : namestrcpy(&rel->rd_rel->relname, relname);
3601 : 58741 : rel->rd_rel->relnamespace = relnamespace;
3602 : :
4322 rhaas@postgresql.org 3603 : 58741 : rel->rd_rel->relkind = relkind;
8325 tgl@sss.pgh.pa.us 3604 : 58741 : rel->rd_rel->relnatts = natts;
3605 : 58741 : rel->rd_rel->reltype = InvalidOid;
3606 : : /* needed when bootstrapping: */
6806 3607 : 58741 : rel->rd_rel->relowner = BOOTSTRAP_SUPERUSERID;
3608 : :
3609 : : /* set up persistence and relcache fields dependent on it */
4871 rhaas@postgresql.org 3610 : 58741 : rel->rd_rel->relpersistence = relpersistence;
3611 [ + + - ]: 58741 : switch (relpersistence)
3612 : : {
4855 3613 : 55847 : case RELPERSISTENCE_UNLOGGED:
3614 : : case RELPERSISTENCE_PERMANENT:
42 heikki.linnakangas@i 3615 :GNC 55847 : rel->rd_backend = INVALID_PROC_NUMBER;
4136 tgl@sss.pgh.pa.us 3616 :CBC 55847 : rel->rd_islocaltemp = false;
4871 rhaas@postgresql.org 3617 : 55847 : break;
3618 : 2894 : case RELPERSISTENCE_TEMP:
3520 bruce@momjian.us 3619 [ - + ]: 2894 : Assert(isTempOrTempToastNamespace(relnamespace));
42 heikki.linnakangas@i 3620 [ + - ]:GNC 2894 : rel->rd_backend = ProcNumberForTempRelations();
4136 tgl@sss.pgh.pa.us 3621 :CBC 2894 : rel->rd_islocaltemp = true;
4871 rhaas@postgresql.org 3622 : 2894 : break;
4871 rhaas@postgresql.org 3623 :UBC 0 : default:
3624 [ # # ]: 0 : elog(ERROR, "invalid relpersistence: %c", relpersistence);
3625 : : break;
3626 : : }
3627 : :
3628 : : /* if it's a materialized view, it's not populated initially */
3996 tgl@sss.pgh.pa.us 3629 [ + + ]:CBC 58741 : if (relkind == RELKIND_MATVIEW)
3630 : 219 : rel->rd_rel->relispopulated = false;
3631 : : else
3632 : 58522 : rel->rd_rel->relispopulated = true;
3633 : :
3634 : : /* set replica identity -- system catalogs and non-tables don't have one */
1803 3635 [ + + + + ]: 58741 : if (!IsCatalogNamespace(relnamespace) &&
2685 rhaas@postgresql.org 3636 [ + + ]: 32119 : (relkind == RELKIND_RELATION ||
3637 [ + + ]: 31900 : relkind == RELKIND_MATVIEW ||
3638 : : relkind == RELKIND_PARTITIONED_TABLE))
3810 3639 : 18946 : rel->rd_rel->relreplident = REPLICA_IDENTITY_DEFAULT;
3640 : : else
3641 : 39795 : rel->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING;
3642 : :
3643 : : /*
3644 : : * Insert relation physical and logical identifiers (OIDs) into the right
3645 : : * places. For a mapped relation, we set relfilenumber to zero and rely
3646 : : * on RelationInitPhysicalAddr to consult the map.
3647 : : */
7240 tgl@sss.pgh.pa.us 3648 : 58741 : rel->rd_rel->relisshared = shared_relation;
3649 : :
8325 3650 : 58741 : RelationGetRelid(rel) = relid;
3651 : :
3652 [ + + ]: 249118 : for (i = 0; i < natts; i++)
2429 andres@anarazel.de 3653 : 190377 : TupleDescAttr(rel->rd_att, i)->attrelid = relid;
3654 : :
7240 tgl@sss.pgh.pa.us 3655 : 58741 : rel->rd_rel->reltablespace = reltablespace;
3656 : :
5180 3657 [ + + ]: 58741 : if (mapped_relation)
3658 : : {
648 rhaas@postgresql.org 3659 : 2694 : rel->rd_rel->relfilenode = InvalidRelFileNumber;
3660 : : /* Add it to the active mapping information */
3661 : 2694 : RelationMapUpdateMap(relid, relfilenumber, shared_relation, true);
3662 : : }
3663 : : else
3664 : 56047 : rel->rd_rel->relfilenode = relfilenumber;
3665 : :
8055 tgl@sss.pgh.pa.us 3666 : 58741 : RelationInitLockInfo(rel); /* see lmgr.c */
3667 : :
7240 3668 : 58741 : RelationInitPhysicalAddr(rel);
3669 : :
1866 andres@anarazel.de 3670 : 58741 : rel->rd_rel->relam = accessmtd;
3671 : :
3672 : : /*
3673 : : * RelationInitTableAccessMethod will do syscache lookups, so we mustn't
3674 : : * run it in CacheMemoryContext. Fortunately, the remaining steps don't
3675 : : * require a long-lived current context.
3676 : : */
1123 tgl@sss.pgh.pa.us 3677 : 58741 : MemoryContextSwitchTo(oldcxt);
3678 : :
863 peter@eisentraut.org 3679 [ + + + + : 58741 : if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_SEQUENCE)
+ + + + ]
1866 andres@anarazel.de 3680 : 27872 : RelationInitTableAccessMethod(rel);
3681 : :
3682 : : /*
3683 : : * Okay to insert into the relcache hash table.
3684 : : *
3685 : : * Ordinarily, there should certainly not be an existing hash entry for
3686 : : * the same OID; but during bootstrap, when we create a "real" relcache
3687 : : * entry for one of the bootstrap relations, we'll be overwriting the
3688 : : * phony one created with formrdesc. So allow that to happen for nailed
3689 : : * rels.
3690 : : */
3619 tgl@sss.pgh.pa.us 3691 [ + + - + : 58741 : RelationCacheInsert(rel, nailit);
- + - + -
- ]
3692 : :
3693 : : /*
3694 : : * Flag relation as needing eoxact cleanup (to clear rd_createSubid). We
3695 : : * can't do this before storing relid in it.
3696 : : */
4102 3697 [ + + ]: 58741 : EOXactListAdd(rel);
3698 : :
3699 : : /* It's fully valid */
7169 3700 : 58741 : rel->rd_isvalid = true;
3701 : :
3702 : : /*
3703 : : * Caller expects us to pin the returned entry.
3704 : : */
7211 3705 : 58741 : RelationIncrementReferenceCount(rel);
3706 : :
8325 3707 : 58741 : return rel;
3708 : : }
3709 : :
3710 : :
3711 : : /*
3712 : : * RelationSetNewRelfilenumber
3713 : : *
3714 : : * Assign a new relfilenumber (physical file name), and possibly a new
3715 : : * persistence setting, to the relation.
3716 : : *
3717 : : * This allows a full rewrite of the relation to be done with transactional
3718 : : * safety (since the filenumber assignment can be rolled back). Note however
3719 : : * that there is no simple way to access the relation's old data for the
3720 : : * remainder of the current transaction. This limits the usefulness to cases
3721 : : * such as TRUNCATE or rebuilding an index from scratch.
3722 : : *
3723 : : * Caller must already hold exclusive lock on the relation.
3724 : : */
3725 : : void
648 rhaas@postgresql.org 3726 : 5807 : RelationSetNewRelfilenumber(Relation relation, char persistence)
3727 : : {
3728 : : RelFileNumber newrelfilenumber;
3729 : : Relation pg_class;
3730 : : HeapTuple tuple;
3731 : : Form_pg_class classform;
1844 andres@anarazel.de 3732 : 5807 : MultiXactId minmulti = InvalidMultiXactId;
3733 : 5807 : TransactionId freezeXid = InvalidTransactionId;
3734 : : RelFileLocator newrlocator;
3735 : :
626 rhaas@postgresql.org 3736 [ + + ]: 5807 : if (!IsBinaryUpgrade)
3737 : : {
3738 : : /* Allocate a new relfilenumber */
3739 : 5787 : newrelfilenumber = GetNewRelFileNumber(relation->rd_rel->reltablespace,
3740 : : NULL, persistence);
3741 : : }
3742 [ + + ]: 20 : else if (relation->rd_rel->relkind == RELKIND_INDEX)
3743 : : {
3744 [ - + ]: 10 : if (!OidIsValid(binary_upgrade_next_index_pg_class_relfilenumber))
626 rhaas@postgresql.org 3745 [ # # ]:UBC 0 : ereport(ERROR,
3746 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3747 : : errmsg("index relfilenumber value not set when in binary upgrade mode")));
3748 : :
626 rhaas@postgresql.org 3749 :CBC 10 : newrelfilenumber = binary_upgrade_next_index_pg_class_relfilenumber;
3750 : 10 : binary_upgrade_next_index_pg_class_relfilenumber = InvalidOid;
3751 : : }
3752 [ + - ]: 10 : else if (relation->rd_rel->relkind == RELKIND_RELATION)
3753 : : {
3754 [ - + ]: 10 : if (!OidIsValid(binary_upgrade_next_heap_pg_class_relfilenumber))
626 rhaas@postgresql.org 3755 [ # # ]:UBC 0 : ereport(ERROR,
3756 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3757 : : errmsg("heap relfilenumber value not set when in binary upgrade mode")));
3758 : :
626 rhaas@postgresql.org 3759 :CBC 10 : newrelfilenumber = binary_upgrade_next_heap_pg_class_relfilenumber;
3760 : 10 : binary_upgrade_next_heap_pg_class_relfilenumber = InvalidOid;
3761 : : }
3762 : : else
626 rhaas@postgresql.org 3763 [ # # ]:UBC 0 : ereport(ERROR,
3764 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3765 : : errmsg("unexpected request for new relfilenumber in binary upgrade mode")));
3766 : :
3767 : : /*
3768 : : * Get a writable copy of the pg_class tuple for the given relation.
3769 : : */
1910 andres@anarazel.de 3770 :CBC 5807 : pg_class = table_open(RelationRelationId, RowExclusiveLock);
3771 : :
5173 rhaas@postgresql.org 3772 : 5807 : tuple = SearchSysCacheCopy1(RELOID,
3773 : : ObjectIdGetDatum(RelationGetRelid(relation)));
5184 tgl@sss.pgh.pa.us 3774 [ - + ]: 5807 : if (!HeapTupleIsValid(tuple))
5184 tgl@sss.pgh.pa.us 3775 [ # # ]:UBC 0 : elog(ERROR, "could not find tuple for relation %u",
3776 : : RelationGetRelid(relation));
5184 tgl@sss.pgh.pa.us 3777 :CBC 5807 : classform = (Form_pg_class) GETSTRUCT(tuple);
3778 : :
3779 : : /*
3780 : : * Schedule unlinking of the old storage at transaction commit, except
3781 : : * when performing a binary upgrade, when we must do it immediately.
3782 : : */
626 rhaas@postgresql.org 3783 [ + + ]: 5807 : if (IsBinaryUpgrade)
3784 : : {
3785 : : SMgrRelation srel;
3786 : :
3787 : : /*
3788 : : * During a binary upgrade, we use this code path to ensure that
3789 : : * pg_largeobject and its index have the same relfilenumbers as in the
3790 : : * old cluster. This is necessary because pg_upgrade treats
3791 : : * pg_largeobject like a user table, not a system table. It is however
3792 : : * possible that a table or index may need to end up with the same
3793 : : * relfilenumber in the new cluster as what it had in the old cluster.
3794 : : * Hence, we can't wait until commit time to remove the old storage.
3795 : : *
3796 : : * In general, this function needs to have transactional semantics,
3797 : : * and removing the old storage before commit time surely isn't.
3798 : : * However, it doesn't really matter, because if a binary upgrade
3799 : : * fails at this stage, the new cluster will need to be recreated
3800 : : * anyway.
3801 : : */
3802 : 20 : srel = smgropen(relation->rd_locator, relation->rd_backend);
3803 : 20 : smgrdounlinkall(&srel, 1, false);
3804 : 20 : smgrclose(srel);
3805 : : }
3806 : : else
3807 : : {
3808 : : /* Not a binary upgrade, so just schedule it to happen later. */
3809 : 5787 : RelationDropStorage(relation);
3810 : : }
3811 : :
3812 : : /*
3813 : : * Create storage for the main fork of the new relfilenumber. If it's a
3814 : : * table-like object, call into the table AM to do so, which'll also
3815 : : * create the table's init fork if needed.
3816 : : *
3817 : : * NOTE: If relevant for the AM, any conflict in relfilenumber value will
3818 : : * be caught here, if GetNewRelFileNumber messes up for any reason.
3819 : : */
648 3820 : 5807 : newrlocator = relation->rd_locator;
3821 : 5807 : newrlocator.relNumber = newrelfilenumber;
3822 : :
863 peter@eisentraut.org 3823 [ + + + + : 5807 : if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))
- + ]
3824 : : {
648 rhaas@postgresql.org 3825 : 2094 : table_relation_set_new_filelocator(relation, &newrlocator,
3826 : : persistence,
3827 : : &freezeXid, &minmulti);
3828 : : }
863 peter@eisentraut.org 3829 [ + - + + : 3713 : else if (RELKIND_HAS_STORAGE(relation->rd_rel->relkind))
- + - - -
- ]
3830 : 3713 : {
3831 : : /* handle these directly, at least for now */
3832 : : SMgrRelation srel;
3833 : :
648 rhaas@postgresql.org 3834 : 3713 : srel = RelationCreateStorage(newrlocator, persistence, true);
863 peter@eisentraut.org 3835 : 3713 : smgrclose(srel);
3836 : : }
3837 : : else
3838 : : {
3839 : : /* we shouldn't be called for anything else */
863 peter@eisentraut.org 3840 [ # # ]:UBC 0 : elog(ERROR, "relation \"%s\" does not have storage",
3841 : : RelationGetRelationName(relation));
3842 : : }
3843 : :
3844 : : /*
3845 : : * If we're dealing with a mapped index, pg_class.relfilenode doesn't
3846 : : * change; instead we have to send the update to the relation mapper.
3847 : : *
3848 : : * For mapped indexes, we don't actually change the pg_class entry at all;
3849 : : * this is essential when reindexing pg_class itself. That leaves us with
3850 : : * possibly-inaccurate values of relpages etc, but those will be fixed up
3851 : : * later.
3852 : : */
1812 andres@anarazel.de 3853 [ + + + + :CBC 5807 : if (RelationIsMapped(relation))
+ + - + -
- + + ]
3854 : : {
3855 : : /* This case is only supported for indexes */
1809 tgl@sss.pgh.pa.us 3856 [ - + ]: 425 : Assert(relation->rd_rel->relkind == RELKIND_INDEX);
3857 : :
3858 : : /* Since we're not updating pg_class, these had better not change */
3859 [ - + ]: 425 : Assert(classform->relfrozenxid == freezeXid);
3860 [ - + ]: 425 : Assert(classform->relminmxid == minmulti);
3861 [ - + ]: 425 : Assert(classform->relpersistence == persistence);
3862 : :
3863 : : /*
3864 : : * In some code paths it's possible that the tuple update we'd
3865 : : * otherwise do here is the only thing that would assign an XID for
3866 : : * the current transaction. However, we must have an XID to delete
3867 : : * files, so make sure one is assigned.
3868 : : */
3869 : 425 : (void) GetCurrentTransactionId();
3870 : :
3871 : : /* Do the deed */
1812 andres@anarazel.de 3872 : 425 : RelationMapUpdateMap(RelationGetRelid(relation),
3873 : : newrelfilenumber,
3874 : 425 : relation->rd_rel->relisshared,
3875 : : false);
3876 : :
3877 : : /* Since we're not updating pg_class, must trigger inval manually */
1809 tgl@sss.pgh.pa.us 3878 : 425 : CacheInvalidateRelcache(relation);
3879 : : }
3880 : : else
3881 : : {
3882 : : /* Normal case, update the pg_class entry */
648 rhaas@postgresql.org 3883 : 5382 : classform->relfilenode = newrelfilenumber;
3884 : :
3885 : : /* relpages etc. never change for sequences */
1809 tgl@sss.pgh.pa.us 3886 [ + + ]: 5382 : if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
3887 : : {
3888 : 5253 : classform->relpages = 0; /* it's empty until further notice */
1323 3889 : 5253 : classform->reltuples = -1;
1809 3890 : 5253 : classform->relallvisible = 0;
3891 : : }
3892 : 5382 : classform->relfrozenxid = freezeXid;
3893 : 5382 : classform->relminmxid = minmulti;
3894 : 5382 : classform->relpersistence = persistence;
3895 : :
3896 : 5382 : CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3897 : : }
3898 : :
5184 3899 : 5807 : heap_freetuple(tuple);
3900 : :
1910 andres@anarazel.de 3901 : 5807 : table_close(pg_class, RowExclusiveLock);
3902 : :
3903 : : /*
3904 : : * Make the pg_class row change or relation map change visible. This will
3905 : : * cause the relcache entry to get updated, too.
3906 : : */
5184 tgl@sss.pgh.pa.us 3907 : 5807 : CommandCounterIncrement();
3908 : :
648 rhaas@postgresql.org 3909 : 5807 : RelationAssumeNewRelfilelocator(relation);
1471 noah@leadboat.com 3910 : 5807 : }
3911 : :
3912 : : /*
3913 : : * RelationAssumeNewRelfilelocator
3914 : : *
3915 : : * Code that modifies pg_class.reltablespace or pg_class.relfilenode must call
3916 : : * this. The call shall precede any code that might insert WAL records whose
3917 : : * replay would modify bytes in the new RelFileLocator, and the call shall follow
3918 : : * any WAL modifying bytes in the prior RelFileLocator. See struct RelationData.
3919 : : * Ideally, call this as near as possible to the CommandCounterIncrement()
3920 : : * that makes the pg_class change visible (before it or after it); that
3921 : : * minimizes the chance of future development adding a forbidden WAL insertion
3922 : : * between RelationAssumeNewRelfilelocator() and CommandCounterIncrement().
3923 : : */
3924 : : void
648 rhaas@postgresql.org 3925 : 6826 : RelationAssumeNewRelfilelocator(Relation relation)
3926 : : {
3927 : 6826 : relation->rd_newRelfilelocatorSubid = GetCurrentSubTransactionId();
3928 [ + + ]: 6826 : if (relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId)
3929 : 6786 : relation->rd_firstRelfilelocatorSubid = relation->rd_newRelfilelocatorSubid;
3930 : :
3931 : : /* Flag relation as needing eoxact cleanup (to clear these fields) */
4102 tgl@sss.pgh.pa.us 3932 [ + + ]: 6826 : EOXactListAdd(relation);
5184 3933 : 6826 : }
3934 : :
3935 : :
3936 : : /*
3937 : : * RelationCacheInitialize
3938 : : *
3939 : : * This initializes the relation descriptor cache. At the time
3940 : : * that this is invoked, we can't do database access yet (mainly
3941 : : * because the transaction subsystem is not up); all we are doing
3942 : : * is making an empty cache hashtable. This must be done before
3943 : : * starting the initialization transaction, because otherwise
3944 : : * AtEOXact_RelationCache would crash if that transaction aborts
3945 : : * before we can get the relcache set up.
3946 : : */
3947 : :
3948 : : #define INITRELCACHESIZE 400
3949 : :
3950 : : void
8652 3951 : 16403 : RelationCacheInitialize(void)
3952 : : {
3953 : : HASHCTL ctl;
3954 : : int allocsize;
3955 : :
3956 : : /*
3957 : : * make sure cache memory context exists
3958 : : */
8691 3959 [ + - ]: 16403 : if (!CacheMemoryContext)
3960 : 16403 : CreateCacheMemoryContext();
3961 : :
3962 : : /*
3963 : : * create hashtable that indexes the relcache
3964 : : */
9716 bruce@momjian.us 3965 : 16403 : ctl.keysize = sizeof(Oid);
8231 tgl@sss.pgh.pa.us 3966 : 16403 : ctl.entrysize = sizeof(RelIdCacheEnt);
8227 3967 : 16403 : RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE,
3968 : : &ctl, HASH_ELEM | HASH_BLOBS);
3969 : :
3970 : : /*
3971 : : * reserve enough in_progress_list slots for many cases
3972 : : */
904 noah@leadboat.com 3973 : 16403 : allocsize = 4;
3974 : 16403 : in_progress_list =
3975 : 16403 : MemoryContextAlloc(CacheMemoryContext,
3976 : : allocsize * sizeof(*in_progress_list));
3977 : 16403 : in_progress_list_maxlen = allocsize;
3978 : :
3979 : : /*
3980 : : * relation mapper needs to be initialized too
3981 : : */
5180 tgl@sss.pgh.pa.us 3982 : 16403 : RelationMapInitialize();
6555 3983 : 16403 : }
3984 : :
3985 : : /*
3986 : : * RelationCacheInitializePhase2
3987 : : *
3988 : : * This is called to prepare for access to shared catalogs during startup.
3989 : : * We must at least set up nailed reldescs for pg_database, pg_authid,
3990 : : * pg_auth_members, and pg_shseclabel. Ideally we'd like to have reldescs
3991 : : * for their indexes, too. We attempt to load this information from the
3992 : : * shared relcache init file. If that's missing or broken, just make
3993 : : * phony entries for the catalogs themselves.
3994 : : * RelationCacheInitializePhase3 will clean up as needed.
3995 : : */
3996 : : void
3997 : 16403 : RelationCacheInitializePhase2(void)
3998 : : {
3999 : : MemoryContext oldcxt;
4000 : :
4001 : : /*
4002 : : * relation mapper needs initialized too
4003 : : */
5180 4004 : 16403 : RelationMapInitializePhase2();
4005 : :
4006 : : /*
4007 : : * In bootstrap mode, the shared catalogs aren't there yet anyway, so do
4008 : : * nothing.
4009 : : */
5359 4010 [ + + ]: 16403 : if (IsBootstrapProcessingMode())
4011 : 39 : return;
4012 : :
4013 : : /*
4014 : : * switch to cache memory context
4015 : : */
4016 : 16364 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4017 : :
4018 : : /*
4019 : : * Try to load the shared relcache cache file. If unsuccessful, bootstrap
4020 : : * the cache with pre-made descriptors for the critical shared catalogs.
4021 : : */
4022 [ + + ]: 16364 : if (!load_relcache_init_file(true))
4023 : : {
5314 4024 : 2904 : formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
4025 : : Natts_pg_database, Desc_pg_database);
5108 4026 : 2904 : formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true,
4027 : : Natts_pg_authid, Desc_pg_authid);
4028 : 2904 : formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true,
4029 : : Natts_pg_auth_members, Desc_pg_auth_members);
3022 alvherre@alvh.no-ip. 4030 : 2904 : formrdesc("pg_shseclabel", SharedSecLabelRelation_Rowtype_Id, true,
4031 : : Natts_pg_shseclabel, Desc_pg_shseclabel);
2642 peter_e@gmx.net 4032 : 2904 : formrdesc("pg_subscription", SubscriptionRelation_Rowtype_Id, true,
4033 : : Natts_pg_subscription, Desc_pg_subscription);
4034 : :
4035 : : #define NUM_CRITICAL_SHARED_RELS 5 /* fix if you change list above */
4036 : : }
4037 : :
5359 tgl@sss.pgh.pa.us 4038 : 16364 : MemoryContextSwitchTo(oldcxt);
4039 : : }
4040 : :
4041 : : /*
4042 : : * RelationCacheInitializePhase3
4043 : : *
4044 : : * This is called as soon as the catcache and transaction system
4045 : : * are functional and we have determined MyDatabaseId. At this point
4046 : : * we can actually read data from the database's system catalogs.
4047 : : * We first try to read pre-computed relcache entries from the local
4048 : : * relcache init file. If that's missing or broken, make phony entries
4049 : : * for the minimum set of nailed-in-cache relations. Then (unless
4050 : : * bootstrapping) make sure we have entries for the critical system
4051 : : * indexes. Once we've done all this, we have enough infrastructure to
4052 : : * open any system catalog or use any catcache. The last step is to
4053 : : * rewrite the cache files if needed.
4054 : : */
4055 : : void
4056 : 13253 : RelationCacheInitializePhase3(void)
4057 : : {
4058 : : HASH_SEQ_STATUS status;
4059 : : RelIdCacheEnt *idhentry;
4060 : : MemoryContext oldcxt;
4061 : 13253 : bool needNewCacheFile = !criticalSharedRelcachesBuilt;
4062 : :
4063 : : /*
4064 : : * relation mapper needs initialized too
4065 : : */
5180 4066 : 13253 : RelationMapInitializePhase3();
4067 : :
4068 : : /*
4069 : : * switch to cache memory context
4070 : : */
6555 4071 : 13253 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4072 : :
4073 : : /*
4074 : : * Try to load the local relcache cache file. If unsuccessful, bootstrap
4075 : : * the cache with pre-made descriptors for the critical "nailed-in" system
4076 : : * catalogs.
4077 : : */
8090 4078 [ + + ]: 13253 : if (IsBootstrapProcessingMode() ||
5359 4079 [ + + ]: 13214 : !load_relcache_init_file(false))
4080 : : {
6553 4081 : 1438 : needNewCacheFile = true;
4082 : :
5314 4083 : 1438 : formrdesc("pg_class", RelationRelation_Rowtype_Id, false,
4084 : : Natts_pg_class, Desc_pg_class);
4085 : 1438 : formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false,
4086 : : Natts_pg_attribute, Desc_pg_attribute);
4087 : 1438 : formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false,
4088 : : Natts_pg_proc, Desc_pg_proc);
4089 : 1438 : formrdesc("pg_type", TypeRelation_Rowtype_Id, false,
4090 : : Natts_pg_type, Desc_pg_type);
4091 : :
4092 : : #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */
4093 : : }
4094 : :
9716 bruce@momjian.us 4095 : 13252 : MemoryContextSwitchTo(oldcxt);
4096 : :
4097 : : /* In bootstrap mode, the faked-up formrdesc info is all we'll have */
8090 tgl@sss.pgh.pa.us 4098 [ + + ]: 13252 : if (IsBootstrapProcessingMode())
4099 : 39 : return;
4100 : :
4101 : : /*
4102 : : * If we didn't get the critical system indexes loaded into relcache, do
4103 : : * so now. These are critical because the catcache and/or opclass cache
4104 : : * depend on them for fetches done during relcache load. Thus, we have an
4105 : : * infinite-recursion problem. We can break the recursion by doing
4106 : : * heapscans instead of indexscans at certain key spots. To avoid hobbling
4107 : : * performance, we only want to do that until we have the critical indexes
4108 : : * loaded into relcache. Thus, the flag criticalRelcachesBuilt is used to
4109 : : * decide whether to do heapscan or indexscan at the key spots, and we set
4110 : : * it true after we've loaded the critical indexes.
4111 : : *
4112 : : * The critical indexes are marked as "nailed in cache", partly to make it
4113 : : * easy for load_relcache_init_file to count them, but mainly because we
4114 : : * cannot flush and rebuild them once we've set criticalRelcachesBuilt to
4115 : : * true. (NOTE: perhaps it would be possible to reload them by
4116 : : * temporarily setting criticalRelcachesBuilt to false again. For now,
4117 : : * though, we just nail 'em in.)
4118 : : *
4119 : : * RewriteRelRulenameIndexId and TriggerRelidNameIndexId are not critical
4120 : : * in the same way as the others, because the critical catalogs don't
4121 : : * (currently) have any rules or triggers, and so these indexes can be
4122 : : * rebuilt without inducing recursion. However they are used during
4123 : : * relcache load when a rel does have rules or triggers, so we choose to
4124 : : * nail them for performance reasons.
4125 : : */
7893 bruce@momjian.us 4126 [ + + ]: 13213 : if (!criticalRelcachesBuilt)
4127 : : {
5205 tgl@sss.pgh.pa.us 4128 : 1399 : load_critical_index(ClassOidIndexId,
4129 : : RelationRelationId);
4130 : 1396 : load_critical_index(AttributeRelidNumIndexId,
4131 : : AttributeRelationId);
4132 : 1396 : load_critical_index(IndexRelidIndexId,
4133 : : IndexRelationId);
4134 : 1396 : load_critical_index(OpclassOidIndexId,
4135 : : OperatorClassRelationId);
4136 : 1396 : load_critical_index(AccessMethodProcedureIndexId,
4137 : : AccessMethodProcedureRelationId);
4138 : 1396 : load_critical_index(RewriteRelRulenameIndexId,
4139 : : RewriteRelationId);
4140 : 1395 : load_critical_index(TriggerRelidNameIndexId,
4141 : : TriggerRelationId);
4142 : :
4143 : : #define NUM_CRITICAL_LOCAL_INDEXES 7 /* fix if you change list above */
4144 : :
8090 4145 : 1395 : criticalRelcachesBuilt = true;
4146 : : }
4147 : :
4148 : : /*
4149 : : * Process critical shared indexes too.
4150 : : *
4151 : : * DatabaseNameIndexId isn't critical for relcache loading, but rather for
4152 : : * initial lookup of MyDatabaseId, without which we'll never find any
4153 : : * non-shared catalogs at all. Autovacuum calls InitPostgres with a
4154 : : * database OID, so it instead depends on DatabaseOidIndexId. We also
4155 : : * need to nail up some indexes on pg_authid and pg_auth_members for use
4156 : : * during client authentication. SharedSecLabelObjectIndexId isn't
4157 : : * critical for the core system, but authentication hooks might be
4158 : : * interested in it.
4159 : : */
5359 4160 [ + + ]: 13209 : if (!criticalSharedRelcachesBuilt)
4161 : : {
5205 4162 : 1154 : load_critical_index(DatabaseNameIndexId,
4163 : : DatabaseRelationId);
4164 : 1154 : load_critical_index(DatabaseOidIndexId,
4165 : : DatabaseRelationId);
5108 4166 : 1154 : load_critical_index(AuthIdRolnameIndexId,
4167 : : AuthIdRelationId);
4168 : 1154 : load_critical_index(AuthIdOidIndexId,
4169 : : AuthIdRelationId);
4170 : 1154 : load_critical_index(AuthMemMemRoleIndexId,
4171 : : AuthMemRelationId);
3022 alvherre@alvh.no-ip. 4172 : 1154 : load_critical_index(SharedSecLabelObjectIndexId,
4173 : : SharedSecLabelRelationId);
4174 : :
4175 : : #define NUM_CRITICAL_SHARED_INDEXES 6 /* fix if you change list above */
4176 : :
5359 tgl@sss.pgh.pa.us 4177 : 1154 : criticalSharedRelcachesBuilt = true;
4178 : : }
4179 : :
4180 : : /*
4181 : : * Now, scan all the relcache entries and update anything that might be
4182 : : * wrong in the results from formrdesc or the relcache cache file. If we
4183 : : * faked up relcache entries using formrdesc, then read the real pg_class
4184 : : * rows and replace the fake entries with them. Also, if any of the
4185 : : * relcache entries have rules, triggers, or security policies, load that
4186 : : * info the hard way since it isn't recorded in the cache file.
4187 : : *
4188 : : * Whenever we access the catalogs to read data, there is a possibility of
4189 : : * a shared-inval cache flush causing relcache entries to be removed.
4190 : : * Since hash_seq_search only guarantees to still work after the *current*
4191 : : * entry is removed, it's unsafe to continue the hashtable scan afterward.
4192 : : * We handle this by restarting the scan from scratch after each access.
4193 : : * This is theoretically O(N^2), but the number of entries that actually
4194 : : * need to be fixed is small enough that it doesn't matter.
4195 : : */
8055 4196 : 13209 : hash_seq_init(&status, RelationIdCache);
4197 : :
4198 [ + + ]: 1846141 : while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
4199 : : {
4200 : 1819723 : Relation relation = idhentry->reldesc;
5314 4201 : 1819723 : bool restart = false;
4202 : :
4203 : : /*
4204 : : * Make sure *this* entry doesn't get flushed while we work with it.
4205 : : */
4206 : 1819723 : RelationIncrementReferenceCount(relation);
4207 : :
4208 : : /*
4209 : : * If it's a faked-up entry, read the real pg_class tuple.
4210 : : */
4211 [ + + ]: 1819723 : if (relation->rd_rel->relowner == InvalidOid)
4212 : : {
4213 : : HeapTuple htup;
4214 : : Form_pg_class relp;
4215 : :
5173 rhaas@postgresql.org 4216 : 11346 : htup = SearchSysCache1(RELOID,
4217 : : ObjectIdGetDatum(RelationGetRelid(relation)));
8090 tgl@sss.pgh.pa.us 4218 [ - + ]: 11346 : if (!HeapTupleIsValid(htup))
11 dgustafsson@postgres 4219 [ # # ]:UNC 0 : ereport(FATAL,
4220 : : errcode(ERRCODE_UNDEFINED_OBJECT),
4221 : : errmsg_internal("cache lookup failed for relation %u",
4222 : : RelationGetRelid(relation)));
8090 tgl@sss.pgh.pa.us 4223 :CBC 11346 : relp = (Form_pg_class) GETSTRUCT(htup);
4224 : :
4225 : : /*
4226 : : * Copy tuple to relation->rd_rel. (See notes in
4227 : : * AllocateRelationDesc())
4228 : : */
4229 : 11346 : memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
4230 : :
4231 : : /* Update rd_options while we have the tuple */
6495 4232 [ - + ]: 11346 : if (relation->rd_options)
6495 tgl@sss.pgh.pa.us 4233 :UBC 0 : pfree(relation->rd_options);
6495 tgl@sss.pgh.pa.us 4234 :CBC 11346 : RelationParseRelOptions(relation, htup);
4235 : :
4236 : : /*
4237 : : * Check the values in rd_att were set up correctly. (We cannot
4238 : : * just copy them over now: formrdesc must have set up the rd_att
4239 : : * data correctly to start with, because it may already have been
4240 : : * copied into one or more catcache entries.)
4241 : : */
5314 4242 [ - + ]: 11346 : Assert(relation->rd_att->tdtypeid == relp->reltype);
4243 [ - + ]: 11346 : Assert(relation->rd_att->tdtypmod == -1);
4244 : :
8090 4245 : 11346 : ReleaseSysCache(htup);
4246 : :
4247 : : /* relowner had better be OK now, else we'll loop forever */
5314 4248 [ - + ]: 11346 : if (relation->rd_rel->relowner == InvalidOid)
5314 tgl@sss.pgh.pa.us 4249 [ # # ]:UBC 0 : elog(ERROR, "invalid relowner in pg_class entry for \"%s\"",
4250 : : RelationGetRelationName(relation));
4251 : :
5314 tgl@sss.pgh.pa.us 4252 :CBC 11346 : restart = true;
4253 : : }
4254 : :
4255 : : /*
4256 : : * Fix data that isn't saved in relcache cache file.
4257 : : *
4258 : : * relhasrules or relhastriggers could possibly be wrong or out of
4259 : : * date. If we don't actually find any rules or triggers, clear the
4260 : : * local copy of the flag so that we don't get into an infinite loop
4261 : : * here. We don't make any attempt to fix the pg_class entry, though.
4262 : : */
8090 4263 [ - + - - ]: 1819723 : if (relation->rd_rel->relhasrules && relation->rd_rules == NULL)
4264 : : {
8090 tgl@sss.pgh.pa.us 4265 :UBC 0 : RelationBuildRuleLock(relation);
5314 4266 [ # # ]: 0 : if (relation->rd_rules == NULL)
4267 : 0 : relation->rd_rel->relhasrules = false;
4268 : 0 : restart = true;
4269 : : }
5635 tgl@sss.pgh.pa.us 4270 [ - + - - ]:CBC 1819723 : if (relation->rd_rel->relhastriggers && relation->trigdesc == NULL)
4271 : : {
8090 tgl@sss.pgh.pa.us 4272 :UBC 0 : RelationBuildTriggers(relation);
5314 4273 [ # # ]: 0 : if (relation->trigdesc == NULL)
4274 : 0 : relation->rd_rel->relhastriggers = false;
4275 : 0 : restart = true;
4276 : : }
4277 : :
4278 : : /*
4279 : : * Re-load the row security policies if the relation has them, since
4280 : : * they are not preserved in the cache. Note that we can never NOT
4281 : : * have a policy while relrowsecurity is true,
4282 : : * RelationBuildRowSecurity will create a single default-deny policy
4283 : : * if there is no policy defined in pg_policy.
4284 : : */
3439 sfrost@snowman.net 4285 [ - + - - ]:CBC 1819723 : if (relation->rd_rel->relrowsecurity && relation->rd_rsdesc == NULL)
4286 : : {
3495 sfrost@snowman.net 4287 :UBC 0 : RelationBuildRowSecurity(relation);
4288 : :
3249 bruce@momjian.us 4289 [ # # ]: 0 : Assert(relation->rd_rsdesc != NULL);
3495 sfrost@snowman.net 4290 : 0 : restart = true;
4291 : : }
4292 : :
4293 : : /* Reload tableam data if needed */
1866 andres@anarazel.de 4294 [ + + ]:CBC 1819723 : if (relation->rd_tableam == NULL &&
863 peter@eisentraut.org 4295 [ + - + - : 1109835 : (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) || relation->rd_rel->relkind == RELKIND_SEQUENCE))
+ - - + ]
4296 : : {
1866 andres@anarazel.de 4297 :UBC 0 : RelationInitTableAccessMethod(relation);
4298 [ # # ]: 0 : Assert(relation->rd_tableam != NULL);
4299 : :
4300 : 0 : restart = true;
4301 : : }
4302 : :
4303 : : /* Release hold on the relation */
5314 tgl@sss.pgh.pa.us 4304 :CBC 1819723 : RelationDecrementReferenceCount(relation);
4305 : :
4306 : : /* Now, restart the hashtable scan if needed */
4307 [ + + ]: 1819723 : if (restart)
4308 : : {
4309 : 11346 : hash_seq_term(&status);
4310 : 11346 : hash_seq_init(&status, RelationIdCache);
4311 : : }
4312 : : }
4313 : :
4314 : : /*
4315 : : * Lastly, write out new relcache cache files if needed. We don't bother
4316 : : * to distinguish cases where only one of the two needs an update.
4317 : : */
8090 4318 [ + + ]: 13209 : if (needNewCacheFile)
4319 : : {
4320 : : /*
4321 : : * Force all the catcaches to finish initializing and thereby open the
4322 : : * catalogs and indexes they use. This will preload the relcache with
4323 : : * entries for all the most important system catalogs and indexes, so
4324 : : * that the init files will be most useful for future backends.
4325 : : */
4326 : 1445 : InitCatalogCachePhase2();
4327 : :
4328 : : /* now write the files */
5359 4329 : 1445 : write_relcache_init_file(true);
4330 : 1445 : write_relcache_init_file(false);
4331 : : }
4332 : : }
4333 : :
4334 : : /*
4335 : : * Load one critical system index into the relcache
4336 : : *
4337 : : * indexoid is the OID of the target index, heapoid is the OID of the catalog
4338 : : * it belongs to.
4339 : : */
4340 : : static void
5205 4341 : 16698 : load_critical_index(Oid indexoid, Oid heapoid)
4342 : : {
4343 : : Relation ird;
4344 : :
4345 : : /*
4346 : : * We must lock the underlying catalog before locking the index to avoid
4347 : : * deadlock, since RelationBuildDesc might well need to read the catalog,
4348 : : * and if anyone else is exclusive-locking this catalog and index they'll
4349 : : * be doing it in that order.
4350 : : */
4351 : 16698 : LockRelationOid(heapoid, AccessShareLock);
5359 4352 : 16698 : LockRelationOid(indexoid, AccessShareLock);
5206 4353 : 16698 : ird = RelationBuildDesc(indexoid, true);
5359 4354 [ - + ]: 16694 : if (ird == NULL)
11 dgustafsson@postgres 4355 [ # # ]:UNC 0 : ereport(PANIC,
4356 : : errcode(ERRCODE_DATA_CORRUPTED),
4357 : : errmsg_internal("could not open critical system index %u", indexoid));
5359 tgl@sss.pgh.pa.us 4358 :CBC 16694 : ird->rd_isnailed = true;
4359 : 16694 : ird->rd_refcnt = 1;
4360 : 16694 : UnlockRelationOid(indexoid, AccessShareLock);
5205 4361 : 16694 : UnlockRelationOid(heapoid, AccessShareLock);
4362 : :
1476 akorotkov@postgresql 4363 : 16694 : (void) RelationGetIndexAttOptions(ird, false);
5359 tgl@sss.pgh.pa.us 4364 : 16694 : }
4365 : :
4366 : : /*
4367 : : * GetPgClassDescriptor -- get a predefined tuple descriptor for pg_class
4368 : : * GetPgIndexDescriptor -- get a predefined tuple descriptor for pg_index
4369 : : *
4370 : : * We need this kluge because we have to be able to access non-fixed-width
4371 : : * fields of pg_class and pg_index before we have the standard catalog caches
4372 : : * available. We use predefined data that's set up in just the same way as
4373 : : * the bootstrapped reldescs used by formrdesc(). The resulting tupdesc is
4374 : : * not 100% kosher: it does not have the correct rowtype OID in tdtypeid, nor
4375 : : * does it have a TupleConstr field. But it's good enough for the purpose of
4376 : : * extracting fields.
4377 : : */
4378 : : static TupleDesc
1972 andres@anarazel.de 4379 : 26501 : BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs)
4380 : : {
4381 : : TupleDesc result;
4382 : : MemoryContext oldcxt;
4383 : : int i;
4384 : :
6956 tgl@sss.pgh.pa.us 4385 : 26501 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4386 : :
1972 andres@anarazel.de 4387 : 26501 : result = CreateTemplateTupleDesc(natts);
2489 tgl@sss.pgh.pa.us 4388 : 26501 : result->tdtypeid = RECORDOID; /* not right, but we don't care */
6495 4389 : 26501 : result->tdtypmod = -1;
4390 : :
4391 [ + + ]: 742046 : for (i = 0; i < natts; i++)
4392 : : {
2429 andres@anarazel.de 4393 : 715545 : memcpy(TupleDescAttr(result, i), &attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
4394 : : /* make sure attcacheoff is valid */
4395 : 715545 : TupleDescAttr(result, i)->attcacheoff = -1;
4396 : : }
4397 : :
4398 : : /* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */
4399 : 26501 : TupleDescAttr(result, 0)->attcacheoff = 0;
4400 : :
4401 : : /* Note: we don't bother to set up a TupleConstr entry */
4402 : :
6956 tgl@sss.pgh.pa.us 4403 : 26501 : MemoryContextSwitchTo(oldcxt);
4404 : :
6495 4405 : 26501 : return result;
4406 : : }
4407 : :
4408 : : static TupleDesc
4409 : 706455 : GetPgClassDescriptor(void)
4410 : : {
4411 : : static TupleDesc pgclassdesc = NULL;
4412 : :
4413 : : /* Already done? */
4414 [ + + ]: 706455 : if (pgclassdesc == NULL)
4415 : 13252 : pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class,
4416 : : Desc_pg_class);
4417 : :
4418 : 706455 : return pgclassdesc;
4419 : : }
4420 : :
4421 : : static TupleDesc
4422 : 907974 : GetPgIndexDescriptor(void)
4423 : : {
4424 : : static TupleDesc pgindexdesc = NULL;
4425 : :
4426 : : /* Already done? */
4427 [ + + ]: 907974 : if (pgindexdesc == NULL)
4428 : 13249 : pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index,
4429 : : Desc_pg_index);
4430 : :
6956 4431 : 907974 : return pgindexdesc;
4432 : : }
4433 : :
4434 : : /*
4435 : : * Load any default attribute value definitions for the relation.
4436 : : *
4437 : : * ndef is the number of attributes that were marked atthasdef.
4438 : : *
4439 : : * Note: we don't make it a hard error to be missing some pg_attrdef records.
4440 : : * We can limp along as long as nothing needs to use the default value. Code
4441 : : * that fails to find an expected AttrDefault record should throw an error.
4442 : : */
4443 : : static void
1104 4444 : 13607 : AttrDefaultFetch(Relation relation, int ndef)
4445 : : {
4446 : : AttrDefault *attrdef;
4447 : : Relation adrel;
4448 : : SysScanDesc adscan;
4449 : : ScanKeyData skey;
4450 : : HeapTuple htup;
4451 : 13607 : int found = 0;
4452 : :
4453 : : /* Allocate array with room for as many entries as expected */
4454 : : attrdef = (AttrDefault *)
4455 : 13607 : MemoryContextAllocZero(CacheMemoryContext,
4456 : : ndef * sizeof(AttrDefault));
4457 : :
4458 : : /* Search pg_attrdef for relevant entries */
7459 4459 : 13607 : ScanKeyInit(&skey,
4460 : : Anum_pg_attrdef_adrelid,
4461 : : BTEqualStrategyNumber, F_OIDEQ,
4462 : : ObjectIdGetDatum(RelationGetRelid(relation)));
4463 : :
1910 andres@anarazel.de 4464 : 13607 : adrel = table_open(AttrDefaultRelationId, AccessShareLock);
6940 tgl@sss.pgh.pa.us 4465 : 13607 : adscan = systable_beginscan(adrel, AttrDefaultIndexId, true,
4466 : : NULL, 1, &skey);
4467 : :
8090 4468 [ + + ]: 32776 : while (HeapTupleIsValid(htup = systable_getnext(adscan)))
4469 : : {
4470 : 19169 : Form_pg_attrdef adform = (Form_pg_attrdef) GETSTRUCT(htup);
4471 : : Datum val;
4472 : : bool isnull;
4473 : :
4474 : : /* protect limited size of array */
1104 4475 [ - + ]: 19169 : if (found >= ndef)
4476 : : {
1104 tgl@sss.pgh.pa.us 4477 [ # # ]:UBC 0 : elog(WARNING, "unexpected pg_attrdef record found for attribute %d of relation \"%s\"",
4478 : : adform->adnum, RelationGetRelationName(relation));
9716 bruce@momjian.us 4479 : 0 : break;
4480 : : }
4481 : :
1104 tgl@sss.pgh.pa.us 4482 :CBC 19169 : val = fastgetattr(htup,
4483 : : Anum_pg_attrdef_adbin,
4484 : : adrel->rd_att, &isnull);
4485 [ - + ]: 19169 : if (isnull)
1104 tgl@sss.pgh.pa.us 4486 [ # # ]:UBC 0 : elog(WARNING, "null adbin for attribute %d of relation \"%s\"",
4487 : : adform->adnum, RelationGetRelationName(relation));
4488 : : else
4489 : : {
4490 : : /* detoast and convert to cstring in caller's context */
1104 tgl@sss.pgh.pa.us 4491 :CBC 19169 : char *s = TextDatumGetCString(val);
4492 : :
4493 : 19169 : attrdef[found].adnum = adform->adnum;
4494 : 19169 : attrdef[found].adbin = MemoryContextStrdup(CacheMemoryContext, s);
4495 : 19169 : pfree(s);
4496 : 19169 : found++;
4497 : : }
4498 : : }
4499 : :
8090 4500 : 13607 : systable_endscan(adscan);
1910 andres@anarazel.de 4501 : 13607 : table_close(adrel, AccessShareLock);
4502 : :
1104 tgl@sss.pgh.pa.us 4503 [ - + ]: 13607 : if (found != ndef)
1104 tgl@sss.pgh.pa.us 4504 [ # # ]:UBC 0 : elog(WARNING, "%d pg_attrdef record(s) missing for relation \"%s\"",
4505 : : ndef - found, RelationGetRelationName(relation));
4506 : :
4507 : : /*
4508 : : * Sort the AttrDefault entries by adnum, for the convenience of
4509 : : * equalTupleDescs(). (Usually, they already will be in order, but this
4510 : : * might not be so if systable_getnext isn't using an index.)
4511 : : */
1104 tgl@sss.pgh.pa.us 4512 [ + + ]:CBC 13607 : if (found > 1)
4513 : 3052 : qsort(attrdef, found, sizeof(AttrDefault), AttrDefaultCmp);
4514 : :
4515 : : /* Install array only after it's fully valid */
4516 : 13607 : relation->rd_att->constr->defval = attrdef;
4517 : 13607 : relation->rd_att->constr->num_defval = found;
4518 : 13607 : }
4519 : :
4520 : : /*
4521 : : * qsort comparator to sort AttrDefault entries by adnum
4522 : : */
4523 : : static int
4524 : 5562 : AttrDefaultCmp(const void *a, const void *b)
4525 : : {
4526 : 5562 : const AttrDefault *ada = (const AttrDefault *) a;
4527 : 5562 : const AttrDefault *adb = (const AttrDefault *) b;
4528 : :
58 nathan@postgresql.or 4529 :GNC 5562 : return pg_cmp_s16(ada->adnum, adb->adnum);
4530 : : }
4531 : :
4532 : : /*
4533 : : * Load any check constraints for the relation.
4534 : : *
4535 : : * As with defaults, if we don't find the expected number of them, just warn
4536 : : * here. The executor should throw an error if an INSERT/UPDATE is attempted.
4537 : : */
4538 : : static void
7947 tgl@sss.pgh.pa.us 4539 :CBC 5513 : CheckConstraintFetch(Relation relation)
4540 : : {
4541 : : ConstrCheck *check;
1104 4542 : 5513 : int ncheck = relation->rd_rel->relchecks;
4543 : : Relation conrel;
4544 : : SysScanDesc conscan;
4545 : : ScanKeyData skey[1];
4546 : : HeapTuple htup;
7947 4547 : 5513 : int found = 0;
4548 : :
4549 : : /* Allocate array with room for as many entries as expected */
4550 : : check = (ConstrCheck *)
1104 4551 : 5513 : MemoryContextAllocZero(CacheMemoryContext,
4552 : : ncheck * sizeof(ConstrCheck));
4553 : :
4554 : : /* Search pg_constraint for relevant entries */
7459 4555 : 5513 : ScanKeyInit(&skey[0],
4556 : : Anum_pg_constraint_conrelid,
4557 : : BTEqualStrategyNumber, F_OIDEQ,
4558 : : ObjectIdGetDatum(RelationGetRelid(relation)));
4559 : :
1910 andres@anarazel.de 4560 : 5513 : conrel = table_open(ConstraintRelationId, AccessShareLock);
2049 tgl@sss.pgh.pa.us 4561 : 5513 : conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
4562 : : NULL, 1, skey);
4563 : :
7947 4564 [ + + ]: 17249 : while (HeapTupleIsValid(htup = systable_getnext(conscan)))
4565 : : {
4566 : 11736 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
4567 : : Datum val;
4568 : : bool isnull;
4569 : :
4570 : : /* We want check constraints only */
4571 [ + + ]: 11736 : if (conform->contype != CONSTRAINT_CHECK)
4572 : 4700 : continue;
4573 : :
4574 : : /* protect limited size of array */
7569 4575 [ - + ]: 7036 : if (found >= ncheck)
4576 : : {
1104 tgl@sss.pgh.pa.us 4577 [ # # ]:UBC 0 : elog(WARNING, "unexpected pg_constraint record found for relation \"%s\"",
4578 : : RelationGetRelationName(relation));
4579 : 0 : break;
4580 : : }
4581 : :
4701 alvherre@alvh.no-ip. 4582 :CBC 7036 : check[found].ccvalid = conform->convalidated;
4377 4583 : 7036 : check[found].ccnoinherit = conform->connoinherit;
8689 tgl@sss.pgh.pa.us 4584 : 14072 : check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
6756 bruce@momjian.us 4585 : 7036 : NameStr(conform->conname));
4586 : :
4587 : : /* Grab and test conbin is actually set */
8684 tgl@sss.pgh.pa.us 4588 : 7036 : val = fastgetattr(htup,
4589 : : Anum_pg_constraint_conbin,
4590 : : conrel->rd_att, &isnull);
9716 bruce@momjian.us 4591 [ - + ]: 7036 : if (isnull)
1104 tgl@sss.pgh.pa.us 4592 [ # # ]:UBC 0 : elog(WARNING, "null conbin for relation \"%s\"",
4593 : : RelationGetRelationName(relation));
4594 : : else
4595 : : {
4596 : : /* detoast and convert to cstring in caller's context */
1104 tgl@sss.pgh.pa.us 4597 :CBC 7036 : char *s = TextDatumGetCString(val);
4598 : :
4599 : 7036 : check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, s);
4600 : 7036 : pfree(s);
4601 : 7036 : found++;
4602 : : }
4603 : : }
4604 : :
7947 4605 : 5513 : systable_endscan(conscan);
1910 andres@anarazel.de 4606 : 5513 : table_close(conrel, AccessShareLock);
4607 : :
8090 tgl@sss.pgh.pa.us 4608 [ - + ]: 5513 : if (found != ncheck)
1104 tgl@sss.pgh.pa.us 4609 [ # # ]:UBC 0 : elog(WARNING, "%d pg_constraint record(s) missing for relation \"%s\"",
4610 : : ncheck - found, RelationGetRelationName(relation));
4611 : :
4612 : : /*
4613 : : * Sort the records by name. This ensures that CHECKs are applied in a
4614 : : * deterministic order, and it also makes equalTupleDescs() faster.
4615 : : */
1104 tgl@sss.pgh.pa.us 4616 [ + + ]:CBC 5513 : if (found > 1)
4617 : 1254 : qsort(check, found, sizeof(ConstrCheck), CheckConstraintCmp);
4618 : :
4619 : : /* Install array only after it's fully valid */
4620 : 5513 : relation->rd_att->constr->check = check;
4621 : 5513 : relation->rd_att->constr->num_check = found;
3310 4622 : 5513 : }
4623 : :
4624 : : /*
4625 : : * qsort comparator to sort ConstrCheck entries by name
4626 : : */
4627 : : static int
4628 : 1523 : CheckConstraintCmp(const void *a, const void *b)
4629 : : {
4630 : 1523 : const ConstrCheck *ca = (const ConstrCheck *) a;
4631 : 1523 : const ConstrCheck *cb = (const ConstrCheck *) b;
4632 : :
4633 : 1523 : return strcmp(ca->ccname, cb->ccname);
4634 : : }
4635 : :
4636 : : /*
4637 : : * RelationGetFKeyList -- get a list of foreign key info for the relation
4638 : : *
4639 : : * Returns a list of ForeignKeyCacheInfo structs, one per FK constraining
4640 : : * the given relation. This data is a direct copy of relevant fields from
4641 : : * pg_constraint. The list items are in no particular order.
4642 : : *
4643 : : * CAUTION: the returned list is part of the relcache's data, and could
4644 : : * vanish in a relcache entry reset. Callers must inspect or copy it
4645 : : * before doing anything that might trigger a cache flush, such as
4646 : : * system catalog accesses. copyObject() can be used if desired.
4647 : : * (We define it this way because current callers want to filter and
4648 : : * modify the list entries anyway, so copying would be a waste of time.)
4649 : : */
4650 : : List *
2857 4651 : 98105 : RelationGetFKeyList(Relation relation)
4652 : : {
4653 : : List *result;
4654 : : Relation conrel;
4655 : : SysScanDesc conscan;
4656 : : ScanKeyData skey;
4657 : : HeapTuple htup;
4658 : : List *oldlist;
4659 : : MemoryContext oldcxt;
4660 : :
4661 : : /* Quick exit if we already computed the list. */
4662 [ + + ]: 98105 : if (relation->rd_fkeyvalid)
4663 : 590 : return relation->rd_fkeylist;
4664 : :
4665 : : /* Fast path: non-partitioned tables without triggers can't have FKs */
2011 alvherre@alvh.no-ip. 4666 [ + + ]: 97515 : if (!relation->rd_rel->relhastriggers &&
4667 [ + + ]: 96001 : relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2857 tgl@sss.pgh.pa.us 4668 : 91233 : return NIL;
4669 : :
4670 : : /*
4671 : : * We build the list we intend to return (in the caller's context) while
4672 : : * doing the scan. After successfully completing the scan, we copy that
4673 : : * list into the relcache entry. This avoids cache-context memory leakage
4674 : : * if we get some sort of error partway through.
4675 : : */
4676 : 6282 : result = NIL;
4677 : :
4678 : : /* Prepare to scan pg_constraint for entries having conrelid = this rel. */
4679 : 6282 : ScanKeyInit(&skey,
4680 : : Anum_pg_constraint_conrelid,
4681 : : BTEqualStrategyNumber, F_OIDEQ,
4682 : : ObjectIdGetDatum(RelationGetRelid(relation)));
4683 : :
1910 andres@anarazel.de 4684 : 6282 : conrel = table_open(ConstraintRelationId, AccessShareLock);
2049 tgl@sss.pgh.pa.us 4685 : 6282 : conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
4686 : : NULL, 1, &skey);
4687 : :
2857 4688 [ + + ]: 9672 : while (HeapTupleIsValid(htup = systable_getnext(conscan)))
4689 : : {
4690 : 3390 : Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
4691 : : ForeignKeyCacheInfo *info;
4692 : :
4693 : : /* consider only foreign keys */
4694 [ + + ]: 3390 : if (constraint->contype != CONSTRAINT_FOREIGN)
4695 : 2037 : continue;
4696 : :
4697 : 1353 : info = makeNode(ForeignKeyCacheInfo);
1972 andres@anarazel.de 4698 : 1353 : info->conoid = constraint->oid;
2857 tgl@sss.pgh.pa.us 4699 : 1353 : info->conrelid = constraint->conrelid;
4700 : 1353 : info->confrelid = constraint->confrelid;
4701 : :
1913 alvherre@alvh.no-ip. 4702 : 1353 : DeconstructFkConstraintRow(htup, &info->nkeys,
4703 : 1353 : info->conkey,
4704 : 1353 : info->confkey,
4705 : 1353 : info->conpfeqop,
4706 : : NULL, NULL, NULL, NULL);
4707 : :
4708 : : /* Add FK's node to the result list */
2857 tgl@sss.pgh.pa.us 4709 : 1353 : result = lappend(result, info);
4710 : : }
4711 : :
4712 : 6282 : systable_endscan(conscan);
1910 andres@anarazel.de 4713 : 6282 : table_close(conrel, AccessShareLock);
4714 : :
4715 : : /* Now save a copy of the completed list in the relcache entry. */
2857 tgl@sss.pgh.pa.us 4716 : 6282 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4717 : 6282 : oldlist = relation->rd_fkeylist;
4718 : 6282 : relation->rd_fkeylist = copyObject(result);
4719 : 6282 : relation->rd_fkeyvalid = true;
4720 : 6282 : MemoryContextSwitchTo(oldcxt);
4721 : :
4722 : : /* Don't leak the old list, if there is one */
4723 : 6282 : list_free_deep(oldlist);
4724 : :
4725 : 6282 : return result;
4726 : : }
4727 : :
4728 : : /*
4729 : : * RelationGetIndexList -- get a list of OIDs of indexes on this relation
4730 : : *
4731 : : * The index list is created only if someone requests it. We scan pg_index
4732 : : * to find relevant indexes, and add the list to the relcache entry so that
4733 : : * we won't have to compute it again. Note that shared cache inval of a
4734 : : * relcache entry will delete the old list and set rd_indexvalid to false,
4735 : : * so that we must recompute the index list on next request. This handles
4736 : : * creation or deletion of an index.
4737 : : *
4738 : : * Indexes that are marked not indislive are omitted from the returned list.
4739 : : * Such indexes are expected to be dropped momentarily, and should not be
4740 : : * touched at all by any caller of this function.
4741 : : *
4742 : : * The returned list is guaranteed to be sorted in order by OID. This is
4743 : : * needed by the executor, since for index types that we obtain exclusive
4744 : : * locks on when updating the index, all backends must lock the indexes in
4745 : : * the same order or we will get deadlocks (see ExecOpenIndices()). Any
4746 : : * consistent ordering would do, but ordering by OID is easy.
4747 : : *
4748 : : * Since shared cache inval causes the relcache's copy of the list to go away,
4749 : : * we return a copy of the list palloc'd in the caller's context. The caller
4750 : : * may list_free() the returned list after scanning it. This is necessary
4751 : : * since the caller will typically be doing syscache lookups on the relevant
4752 : : * indexes, and syscache lookup could cause SI messages to be processed!
4753 : : *
4754 : : * In exactly the same way, we update rd_pkindex, which is the OID of the
4755 : : * relation's primary key index if any, else InvalidOid; and rd_replidindex,
4756 : : * which is the pg_class OID of an index to be used as the relation's
4757 : : * replication identity index, or InvalidOid if there is no such index.
4758 : : */
4759 : : List *
8702 4760 : 1073291 : RelationGetIndexList(Relation relation)
4761 : : {
4762 : : Relation indrel;
4763 : : SysScanDesc indscan;
4764 : : ScanKeyData skey;
4765 : : HeapTuple htup;
4766 : : List *result;
4767 : : List *oldlist;
3810 rhaas@postgresql.org 4768 : 1073291 : char replident = relation->rd_rel->relreplident;
4769 : 1073291 : Oid pkeyIndex = InvalidOid;
4770 : 1073291 : Oid candidateIndex = InvalidOid;
37 alvherre@alvh.no-ip. 4771 :GNC 1073291 : bool pkdeferrable = false;
4772 : : MemoryContext oldcxt;
4773 : :
4774 : : /* Quick exit if we already computed the list. */
1808 tgl@sss.pgh.pa.us 4775 [ + + ]:CBC 1073291 : if (relation->rd_indexvalid)
7259 neilc@samurai.com 4776 : 919703 : return list_copy(relation->rd_indexlist);
4777 : :
4778 : : /*
4779 : : * We build the list we intend to return (in the caller's context) while
4780 : : * doing the scan. After successfully completing the scan, we copy that
4781 : : * list into the relcache entry. This avoids cache-context memory leakage
4782 : : * if we get some sort of error partway through.
4783 : : */
8702 tgl@sss.pgh.pa.us 4784 : 153588 : result = NIL;
4785 : :
4786 : : /* Prepare to scan pg_index for entries having indrelid = this rel. */
7459 4787 : 153588 : ScanKeyInit(&skey,
4788 : : Anum_pg_index_indrelid,
4789 : : BTEqualStrategyNumber, F_OIDEQ,
4790 : : ObjectIdGetDatum(RelationGetRelid(relation)));
4791 : :
1910 andres@anarazel.de 4792 : 153588 : indrel = table_open(IndexRelationId, AccessShareLock);
6940 tgl@sss.pgh.pa.us 4793 : 153588 : indscan = systable_beginscan(indrel, IndexIndrelidIndexId, true,
4794 : : NULL, 1, &skey);
4795 : :
8090 4796 [ + + ]: 377763 : while (HeapTupleIsValid(htup = systable_getnext(indscan)))
4797 : : {
4798 : 224175 : Form_pg_index index = (Form_pg_index) GETSTRUCT(htup);
4799 : :
4800 : : /*
4801 : : * Ignore any indexes that are currently being dropped. This will
4802 : : * prevent them from being searched, inserted into, or considered in
4803 : : * HOT-safety decisions. It's unsafe to touch such an index at all
4804 : : * since its catalog entries could disappear at any instant.
4805 : : */
1935 peter_e@gmx.net 4806 [ + + ]: 224175 : if (!index->indislive)
4391 simon@2ndQuadrant.co 4807 : 41 : continue;
4808 : :
4809 : : /* add index's OID to result list */
1734 tgl@sss.pgh.pa.us 4810 : 224134 : result = lappend_oid(result, index->indexrelid);
4811 : :
4812 : : /*
4813 : : * Non-unique or predicate indexes aren't interesting for either oid
4814 : : * indexes or replication identity indexes, so don't check them.
4815 : : * Deferred ones are not useful for replication identity either; but
4816 : : * we do include them if they are PKs.
4817 : : */
233 alvherre@alvh.no-ip. 4818 [ + + ]:GNC 224134 : if (!index->indisunique ||
2209 andrew@dunslane.net 4819 [ + + ]:CBC 191974 : !heap_attisnull(htup, Anum_pg_index_indpred, NULL))
3810 rhaas@postgresql.org 4820 : 32247 : continue;
4821 : :
4822 : : /*
4823 : : * Remember primary key index, if any. We do this only if the index
4824 : : * is valid; but if the table is partitioned, then we do it even if
4825 : : * it's invalid.
4826 : : *
4827 : : * The reason for returning invalid primary keys for foreign tables is
4828 : : * because of pg_dump of NOT NULL constraints, and the fact that PKs
4829 : : * remain marked invalid until the partitions' PKs are attached to it.
4830 : : * If we make rd_pkindex invalid, then the attnotnull flag is reset
4831 : : * after the PK is created, which causes the ALTER INDEX ATTACH
4832 : : * PARTITION to fail with 'column ... is not marked NOT NULL'. With
4833 : : * this, dropconstraint_internal() will believe that the columns must
4834 : : * not have attnotnull reset, so the PKs-on-partitions can be attached
4835 : : * correctly, until finally the PK-on-parent is marked valid.
4836 : : *
4837 : : * Also, this doesn't harm anything, because rd_pkindex is not a
4838 : : * "real" index anyway, but a RELKIND_PARTITIONED_INDEX.
4839 : : */
233 alvherre@alvh.no-ip. 4840 [ + + ]:GNC 191887 : if (index->indisprimary &&
4841 [ + + ]: 125784 : (index->indisvalid ||
4842 [ + - ]: 15 : relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
4843 : : {
3810 rhaas@postgresql.org 4844 :CBC 125784 : pkeyIndex = index->indexrelid;
37 alvherre@alvh.no-ip. 4845 :GNC 125784 : pkdeferrable = !index->indimmediate;
4846 : : }
4847 : :
4848 [ + + ]: 191887 : if (!index->indimmediate)
4849 : 84 : continue;
4850 : :
233 4851 [ + + ]: 191803 : if (!index->indisvalid)
4852 : 42 : continue;
4853 : :
4854 : : /* remember explicitly chosen replica index */
3810 rhaas@postgresql.org 4855 [ + + ]:CBC 191761 : if (index->indisreplident)
4856 : 231 : candidateIndex = index->indexrelid;
4857 : : }
4858 : :
8090 tgl@sss.pgh.pa.us 4859 : 153588 : systable_endscan(indscan);
4860 : :
1910 andres@anarazel.de 4861 : 153588 : table_close(indrel, AccessShareLock);
4862 : :
4863 : : /* Sort the result list into OID order, per API spec. */
1734 tgl@sss.pgh.pa.us 4864 : 153588 : list_sort(result, list_oid_cmp);
4865 : :
4866 : : /* Now save a copy of the completed list in the relcache entry. */
8691 4867 : 153588 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
3532 4868 : 153588 : oldlist = relation->rd_indexlist;
7259 neilc@samurai.com 4869 : 153588 : relation->rd_indexlist = list_copy(result);
2642 peter_e@gmx.net 4870 : 153588 : relation->rd_pkindex = pkeyIndex;
37 alvherre@alvh.no-ip. 4871 :GNC 153588 : relation->rd_ispkdeferrable = pkdeferrable;
4872 [ + + + + : 153588 : if (replident == REPLICA_IDENTITY_DEFAULT && OidIsValid(pkeyIndex) && !pkdeferrable)
+ + ]
3623 tgl@sss.pgh.pa.us 4873 :CBC 12616 : relation->rd_replidindex = pkeyIndex;
4874 [ + + + + ]: 140972 : else if (replident == REPLICA_IDENTITY_INDEX && OidIsValid(candidateIndex))
4875 : 231 : relation->rd_replidindex = candidateIndex;
4876 : : else
4877 : 140741 : relation->rd_replidindex = InvalidOid;
1808 4878 : 153588 : relation->rd_indexvalid = true;
8702 4879 : 153588 : MemoryContextSwitchTo(oldcxt);
4880 : :
4881 : : /* Don't leak the old list, if there is one */
3532 4882 : 153588 : list_free(oldlist);
4883 : :
8702 4884 : 153588 : return result;
4885 : : }
4886 : :
4887 : : /*
4888 : : * RelationGetStatExtList
4889 : : * get a list of OIDs of statistics objects on this relation
4890 : : *
4891 : : * The statistics list is created only if someone requests it, in a way
4892 : : * similar to RelationGetIndexList(). We scan pg_statistic_ext to find
4893 : : * relevant statistics, and add the list to the relcache entry so that we
4894 : : * won't have to compute it again. Note that shared cache inval of a
4895 : : * relcache entry will delete the old list and set rd_statvalid to 0,
4896 : : * so that we must recompute the statistics list on next request. This
4897 : : * handles creation or deletion of a statistics object.
4898 : : *
4899 : : * The returned list is guaranteed to be sorted in order by OID, although
4900 : : * this is not currently needed.
4901 : : *
4902 : : * Since shared cache inval causes the relcache's copy of the list to go away,
4903 : : * we return a copy of the list palloc'd in the caller's context. The caller
4904 : : * may list_free() the returned list after scanning it. This is necessary
4905 : : * since the caller will typically be doing syscache lookups on the relevant
4906 : : * statistics, and syscache lookup could cause SI messages to be processed!
4907 : : */
4908 : : List *
2578 alvherre@alvh.no-ip. 4909 : 205743 : RelationGetStatExtList(Relation relation)
4910 : : {
4911 : : Relation indrel;
4912 : : SysScanDesc indscan;
4913 : : ScanKeyData skey;
4914 : : HeapTuple htup;
4915 : : List *result;
4916 : : List *oldlist;
4917 : : MemoryContext oldcxt;
4918 : :
4919 : : /* Quick exit if we already computed the list. */
4920 [ + + ]: 205743 : if (relation->rd_statvalid != 0)
4921 : 154252 : return list_copy(relation->rd_statlist);
4922 : :
4923 : : /*
4924 : : * We build the list we intend to return (in the caller's context) while
4925 : : * doing the scan. After successfully completing the scan, we copy that
4926 : : * list into the relcache entry. This avoids cache-context memory leakage
4927 : : * if we get some sort of error partway through.
4928 : : */
4929 : 51491 : result = NIL;
4930 : :
4931 : : /*
4932 : : * Prepare to scan pg_statistic_ext for entries having stxrelid = this
4933 : : * rel.
4934 : : */
4935 : 51491 : ScanKeyInit(&skey,
4936 : : Anum_pg_statistic_ext_stxrelid,
4937 : : BTEqualStrategyNumber, F_OIDEQ,
4938 : : ObjectIdGetDatum(RelationGetRelid(relation)));
4939 : :
1910 andres@anarazel.de 4940 : 51491 : indrel = table_open(StatisticExtRelationId, AccessShareLock);
2578 alvherre@alvh.no-ip. 4941 : 51491 : indscan = systable_beginscan(indrel, StatisticExtRelidIndexId, true,
4942 : : NULL, 1, &skey);
4943 : :
4944 [ + + ]: 51675 : while (HeapTupleIsValid(htup = systable_getnext(indscan)))
4945 : : {
1916 tgl@sss.pgh.pa.us 4946 : 184 : Oid oid = ((Form_pg_statistic_ext) GETSTRUCT(htup))->oid;
4947 : :
1734 4948 : 184 : result = lappend_oid(result, oid);
4949 : : }
4950 : :
2578 alvherre@alvh.no-ip. 4951 : 51491 : systable_endscan(indscan);
4952 : :
1910 andres@anarazel.de 4953 : 51491 : table_close(indrel, AccessShareLock);
4954 : :
4955 : : /* Sort the result list into OID order, per API spec. */
1734 tgl@sss.pgh.pa.us 4956 : 51491 : list_sort(result, list_oid_cmp);
4957 : :
4958 : : /* Now save a copy of the completed list in the relcache entry. */
2578 alvherre@alvh.no-ip. 4959 : 51491 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4960 : 51491 : oldlist = relation->rd_statlist;
4961 : 51491 : relation->rd_statlist = list_copy(result);
4962 : :
4963 : 51491 : relation->rd_statvalid = true;
4964 : 51491 : MemoryContextSwitchTo(oldcxt);
4965 : :
4966 : : /* Don't leak the old list, if there is one */
4967 : 51491 : list_free(oldlist);
4968 : :
4969 : 51491 : return result;
4970 : : }
4971 : :
4972 : : /*
4973 : : * RelationGetPrimaryKeyIndex -- get OID of the relation's primary key index
4974 : : *
4975 : : * Returns InvalidOid if there is no such index, or if the primary key is
4976 : : * DEFERRABLE.
4977 : : */
4978 : : Oid
2642 peter_e@gmx.net 4979 : 72272 : RelationGetPrimaryKeyIndex(Relation relation)
4980 : : {
4981 : : List *ilist;
4982 : :
1808 tgl@sss.pgh.pa.us 4983 [ - + ]: 72272 : if (!relation->rd_indexvalid)
4984 : : {
4985 : : /* RelationGetIndexList does the heavy lifting. */
2642 peter_e@gmx.net 4986 :UBC 0 : ilist = RelationGetIndexList(relation);
4987 : 0 : list_free(ilist);
1808 tgl@sss.pgh.pa.us 4988 [ # # ]: 0 : Assert(relation->rd_indexvalid);
4989 : : }
4990 : :
37 alvherre@alvh.no-ip. 4991 [ - + ]:GNC 72272 : return relation->rd_ispkdeferrable ? InvalidOid : relation->rd_pkindex;
4992 : : }
4993 : :
4994 : : /*
4995 : : * RelationGetReplicaIndex -- get OID of the relation's replica identity index
4996 : : *
4997 : : * Returns InvalidOid if there is no such index.
4998 : : */
4999 : : Oid
3623 tgl@sss.pgh.pa.us 5000 :CBC 231016 : RelationGetReplicaIndex(Relation relation)
5001 : : {
5002 : : List *ilist;
5003 : :
1808 5004 [ + + ]: 231016 : if (!relation->rd_indexvalid)
5005 : : {
5006 : : /* RelationGetIndexList does the heavy lifting. */
3623 5007 : 2488 : ilist = RelationGetIndexList(relation);
5008 : 2488 : list_free(ilist);
1808 5009 [ - + ]: 2488 : Assert(relation->rd_indexvalid);
5010 : : }
5011 : :
3623 5012 : 231016 : return relation->rd_replidindex;
5013 : : }
5014 : :
5015 : : /*
5016 : : * RelationGetIndexExpressions -- get the index expressions for an index
5017 : : *
5018 : : * We cache the result of transforming pg_index.indexprs into a node tree.
5019 : : * If the rel is not an index or has no expressional columns, we return NIL.
5020 : : * Otherwise, the returned tree is copied into the caller's memory context.
5021 : : * (We don't want to return a pointer to the relcache copy, since it could
5022 : : * disappear due to relcache invalidation.)
5023 : : */
5024 : : List *
7627 5025 : 1907853 : RelationGetIndexExpressions(Relation relation)
5026 : : {
5027 : : List *result;
5028 : : Datum exprsDatum;
5029 : : bool isnull;
5030 : : char *exprsString;
5031 : : MemoryContext oldcxt;
5032 : :
5033 : : /* Quick exit if we already computed the result. */
5034 [ + + ]: 1907853 : if (relation->rd_indexprs)
2593 peter_e@gmx.net 5035 : 1667 : return copyObject(relation->rd_indexprs);
5036 : :
5037 : : /* Quick exit if there is nothing to do. */
7627 tgl@sss.pgh.pa.us 5038 [ + - + + ]: 3812372 : if (relation->rd_indextuple == NULL ||
2209 andrew@dunslane.net 5039 : 1906186 : heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL))
7627 tgl@sss.pgh.pa.us 5040 : 1905445 : return NIL;
5041 : :
5042 : : /*
5043 : : * We build the tree we intend to return in the caller's context. After
5044 : : * successfully completing the work, we copy it into the relcache entry.
5045 : : * This avoids problems if we get some sort of error partway through.
5046 : : */
6956 5047 : 741 : exprsDatum = heap_getattr(relation->rd_indextuple,
5048 : : Anum_pg_index_indexprs,
5049 : : GetPgIndexDescriptor(),
5050 : : &isnull);
7627 5051 [ - + ]: 741 : Assert(!isnull);
5864 5052 : 741 : exprsString = TextDatumGetCString(exprsDatum);
7627 5053 : 741 : result = (List *) stringToNode(exprsString);
5054 : 741 : pfree(exprsString);
5055 : :
5056 : : /*
5057 : : * Run the expressions through eval_const_expressions. This is not just an
5058 : : * optimization, but is necessary, because the planner will be comparing
5059 : : * them to similarly-processed qual clauses, and may fail to detect valid
5060 : : * matches without this. We must not use canonicalize_qual, however,
5061 : : * since these aren't qual expressions.
5062 : : */
5857 5063 : 741 : result = (List *) eval_const_expressions(NULL, (Node *) result);
5064 : :
5065 : : /* May as well fix opfuncids too */
7627 5066 : 741 : fix_opfuncids((Node *) result);
5067 : :
5068 : : /* Now save a copy of the completed tree in the relcache entry. */
5206 5069 : 741 : oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
2593 peter_e@gmx.net 5070 : 741 : relation->rd_indexprs = copyObject(result);
7627 tgl@sss.pgh.pa.us 5071 : 741 : MemoryContextSwitchTo(oldcxt);
5072 : :
5073 : 741 : return result;
5074 : : }
5075 : :
5076 : : /*
5077 : : * RelationGetDummyIndexExpressions -- get dummy expressions for an index
5078 : : *
5079 : : * Return a list of dummy expressions (just Const nodes) with the same
5080 : : * types/typmods/collations as the index's real expressions. This is
5081 : : * useful in situations where we don't want to run any user-defined code.
5082 : : */
5083 : : List *
1596 5084 : 99 : RelationGetDummyIndexExpressions(Relation relation)
5085 : : {
5086 : : List *result;
5087 : : Datum exprsDatum;
5088 : : bool isnull;
5089 : : char *exprsString;
5090 : : List *rawExprs;
5091 : : ListCell *lc;
5092 : :
5093 : : /* Quick exit if there is nothing to do. */
5094 [ + - + + ]: 198 : if (relation->rd_indextuple == NULL ||
5095 : 99 : heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL))
5096 : 90 : return NIL;
5097 : :
5098 : : /* Extract raw node tree(s) from index tuple. */
5099 : 9 : exprsDatum = heap_getattr(relation->rd_indextuple,
5100 : : Anum_pg_index_indexprs,
5101 : : GetPgIndexDescriptor(),
5102 : : &isnull);
5103 [ - + ]: 9 : Assert(!isnull);
5104 : 9 : exprsString = TextDatumGetCString(exprsDatum);
5105 : 9 : rawExprs = (List *) stringToNode(exprsString);
5106 : 9 : pfree(exprsString);
5107 : :
5108 : : /* Construct null Consts; the typlen and typbyval are arbitrary. */
5109 : 9 : result = NIL;
5110 [ + - + + : 18 : foreach(lc, rawExprs)
+ + ]
5111 : : {
5112 : 9 : Node *rawExpr = (Node *) lfirst(lc);
5113 : :
5114 : 9 : result = lappend(result,
5115 : 9 : makeConst(exprType(rawExpr),
5116 : : exprTypmod(rawExpr),
5117 : : exprCollation(rawExpr),
5118 : : 1,
5119 : : (Datum) 0,
5120 : : true,
5121 : : true));
5122 : : }
5123 : :
5124 : 9 : return result;
5125 : : }
5126 : :
5127 : : /*
5128 : : * RelationGetIndexPredicate -- get the index predicate for an index
5129 : : *
5130 : : * We cache the result of transforming pg_index.indpred into an implicit-AND
5131 : : * node tree (suitable for use in planning).
5132 : : * If the rel is not an index or has no predicate, we return NIL.
5133 : : * Otherwise, the returned tree is copied into the caller's memory context.
5134 : : * (We don't want to return a pointer to the relcache copy, since it could
5135 : : * disappear due to relcache invalidation.)
5136 : : */
5137 : : List *
7627 5138 : 1907799 : RelationGetIndexPredicate(Relation relation)
5139 : : {
5140 : : List *result;
5141 : : Datum predDatum;
5142 : : bool isnull;
5143 : : char *predString;
5144 : : MemoryContext oldcxt;
5145 : :
5146 : : /* Quick exit if we already computed the result. */
5147 [ + + ]: 1907799 : if (relation->rd_indpred)
2593 peter_e@gmx.net 5148 : 635 : return copyObject(relation->rd_indpred);
5149 : :
5150 : : /* Quick exit if there is nothing to do. */
7627 tgl@sss.pgh.pa.us 5151 [ + - + + ]: 3814328 : if (relation->rd_indextuple == NULL ||
2209 andrew@dunslane.net 5152 : 1907164 : heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred, NULL))
7627 tgl@sss.pgh.pa.us 5153 : 1906699 : return NIL;
5154 : :
5155 : : /*
5156 : : * We build the tree we intend to return in the caller's context. After
5157 : : * successfully completing the work, we copy it into the relcache entry.
5158 : : * This avoids problems if we get some sort of error partway through.
5159 : : */
6956 5160 : 465 : predDatum = heap_getattr(relation->rd_indextuple,
5161 : : Anum_pg_index_indpred,
5162 : : GetPgIndexDescriptor(),
5163 : : &isnull);
7627 5164 [ - + ]: 465 : Assert(!isnull);
5864 5165 : 465 : predString = TextDatumGetCString(predDatum);
7627 5166 : 465 : result = (List *) stringToNode(predString);
5167 : 465 : pfree(predString);
5168 : :
5169 : : /*
5170 : : * Run the expression through const-simplification and canonicalization.
5171 : : * This is not just an optimization, but is necessary, because the planner
5172 : : * will be comparing it to similarly-processed qual clauses, and may fail
5173 : : * to detect valid matches without this. This must match the processing
5174 : : * done to qual clauses in preprocess_expression()! (We can skip the
5175 : : * stuff involving subqueries, however, since we don't allow any in index
5176 : : * predicates.)
5177 : : */
5857 5178 : 465 : result = (List *) eval_const_expressions(NULL, (Node *) result);
5179 : :
2226 5180 : 465 : result = (List *) canonicalize_qual((Expr *) result, false);
5181 : :
5182 : : /* Also convert to implicit-AND format */
7413 5183 : 465 : result = make_ands_implicit((Expr *) result);
5184 : :
5185 : : /* May as well fix opfuncids too */
7627 5186 : 465 : fix_opfuncids((Node *) result);
5187 : :
5188 : : /* Now save a copy of the completed tree in the relcache entry. */
5206 5189 : 465 : oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
2593 peter_e@gmx.net 5190 : 465 : relation->rd_indpred = copyObject(result);
7627 tgl@sss.pgh.pa.us 5191 : 465 : MemoryContextSwitchTo(oldcxt);
5192 : :
5193 : 465 : return result;
5194 : : }
5195 : :
5196 : : /*
5197 : : * RelationGetIndexAttrBitmap -- get a bitmap of index attribute numbers
5198 : : *
5199 : : * The result has a bit set for each attribute used anywhere in the index
5200 : : * definitions of all the indexes on this relation. (This includes not only
5201 : : * simple index keys, but attributes used in expressions and partial-index
5202 : : * predicates.)
5203 : : *
5204 : : * Depending on attrKind, a bitmap covering attnums for certain columns is
5205 : : * returned:
5206 : : * INDEX_ATTR_BITMAP_KEY Columns in non-partial unique indexes not
5207 : : * in expressions (i.e., usable for FKs)
5208 : : * INDEX_ATTR_BITMAP_PRIMARY_KEY Columns in the table's primary key
5209 : : * (beware: even if PK is deferrable!)
5210 : : * INDEX_ATTR_BITMAP_IDENTITY_KEY Columns in the table's replica identity
5211 : : * index (empty if FULL)
5212 : : * INDEX_ATTR_BITMAP_HOT_BLOCKING Columns that block updates from being HOT
5213 : : * INDEX_ATTR_BITMAP_SUMMARIZED Columns included in summarizing indexes
5214 : : *
5215 : : * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that
5216 : : * we can include system attributes (e.g., OID) in the bitmap representation.
5217 : : *
5218 : : * Deferred indexes are considered for the primary key, but not for replica
5219 : : * identity.
5220 : : *
5221 : : * Caller had better hold at least RowExclusiveLock on the target relation
5222 : : * to ensure it is safe (deadlock-free) for us to take locks on the relation's
5223 : : * indexes. Note that since the introduction of CREATE INDEX CONCURRENTLY,
5224 : : * that lock level doesn't guarantee a stable set of indexes, so we have to
5225 : : * be prepared to retry here in case of a change in the set of indexes.
5226 : : *
5227 : : * The returned result is palloc'd in the caller's memory context and should
5228 : : * be bms_free'd when not needed anymore.
5229 : : */
5230 : : Bitmapset *
3778 rhaas@postgresql.org 5231 : 1212875 : RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
5232 : : {
5233 : : Bitmapset *uindexattrs; /* columns in unique indexes */
5234 : : Bitmapset *pkindexattrs; /* columns in the primary index */
5235 : : Bitmapset *idindexattrs; /* columns in the replica identity */
5236 : : Bitmapset *hotblockingattrs; /* columns with HOT blocking indexes */
5237 : : Bitmapset *summarizedattrs; /* columns with summarizing indexes */
5238 : : List *indexoidlist;
5239 : : List *newindexoidlist;
5240 : : Oid relpkindex;
5241 : : Oid relreplindex;
5242 : : ListCell *l;
5243 : : MemoryContext oldcxt;
5244 : :
5245 : : /* Quick exit if we already computed the result. */
391 tomas.vondra@postgre 5246 [ + + ]: 1212875 : if (relation->rd_attrsvalid)
5247 : : {
3631 bruce@momjian.us 5248 [ + + + + : 1014649 : switch (attrKind)
+ - ]
5249 : : {
3623 tgl@sss.pgh.pa.us 5250 : 245630 : case INDEX_ATTR_BITMAP_KEY:
5251 : 245630 : return bms_copy(relation->rd_keyattr);
2642 peter_e@gmx.net 5252 : 120 : case INDEX_ATTR_BITMAP_PRIMARY_KEY:
5253 : 120 : return bms_copy(relation->rd_pkattr);
3623 tgl@sss.pgh.pa.us 5254 : 289491 : case INDEX_ATTR_BITMAP_IDENTITY_KEY:
5255 : 289491 : return bms_copy(relation->rd_idattr);
391 tomas.vondra@postgre 5256 : 237034 : case INDEX_ATTR_BITMAP_HOT_BLOCKING:
5257 : 237034 : return bms_copy(relation->rd_hotblockingattr);
5258 : 242374 : case INDEX_ATTR_BITMAP_SUMMARIZED:
5259 : 242374 : return bms_copy(relation->rd_summarizedattr);
3778 rhaas@postgresql.org 5260 :UBC 0 : default:
5261 [ # # ]: 0 : elog(ERROR, "unknown attrKind %u", attrKind);
5262 : : }
5263 : : }
5264 : :
5265 : : /* Fast path if definitely no indexes */
6051 tgl@sss.pgh.pa.us 5266 [ + + ]:CBC 198226 : if (!RelationGetForm(relation)->relhasindex)
5267 : 190272 : return NULL;
5268 : :
5269 : : /*
5270 : : * Get cached list of index OIDs. If we have to start over, we do so here.
5271 : : */
2624 5272 : 7954 : restart:
6051 5273 : 7954 : indexoidlist = RelationGetIndexList(relation);
5274 : :
5275 : : /* Fall out if no indexes (but relhasindex was set) */
5276 [ + + ]: 7954 : if (indexoidlist == NIL)
5277 : 596 : return NULL;
5278 : :
5279 : : /*
5280 : : * Copy the rd_pkindex and rd_replidindex values computed by
5281 : : * RelationGetIndexList before proceeding. This is needed because a
5282 : : * relcache flush could occur inside index_open below, resetting the
5283 : : * fields managed by RelationGetIndexList. We need to do the work with
5284 : : * stable values of these fields.
5285 : : */
2642 peter_e@gmx.net 5286 : 7358 : relpkindex = relation->rd_pkindex;
3623 tgl@sss.pgh.pa.us 5287 : 7358 : relreplindex = relation->rd_replidindex;
5288 : :
5289 : : /*
5290 : : * For each index, add referenced attributes to indexattrs.
5291 : : *
5292 : : * Note: we consider all indexes returned by RelationGetIndexList, even if
5293 : : * they are not indisready or indisvalid. This is important because an
5294 : : * index for which CREATE INDEX CONCURRENTLY has just started must be
5295 : : * included in HOT-safety decisions (see README.HOT). If a DROP INDEX
5296 : : * CONCURRENTLY is far enough along that we should ignore the index, it
5297 : : * won't be returned at all by RelationGetIndexList.
5298 : : */
4099 alvherre@alvh.no-ip. 5299 : 7358 : uindexattrs = NULL;
2642 peter_e@gmx.net 5300 : 7358 : pkindexattrs = NULL;
3778 rhaas@postgresql.org 5301 : 7358 : idindexattrs = NULL;
391 tomas.vondra@postgre 5302 : 7358 : hotblockingattrs = NULL;
5303 : 7358 : summarizedattrs = NULL;
6051 tgl@sss.pgh.pa.us 5304 [ + - + + : 19901 : foreach(l, indexoidlist)
+ + ]
5305 : : {
5306 : 12543 : Oid indexOid = lfirst_oid(l);
5307 : : Relation indexDesc;
5308 : : Datum datum;
5309 : : bool isnull;
5310 : : Node *indexExpressions;
5311 : : Node *indexPredicate;
5312 : : int i;
5313 : : bool isKey; /* candidate key */
5314 : : bool isPK; /* primary key */
5315 : : bool isIDKey; /* replica identity index */
5316 : : Bitmapset **attrs;
5317 : :
5318 : 12543 : indexDesc = index_open(indexOid, AccessShareLock);
5319 : :
5320 : : /*
5321 : : * Extract index expressions and index predicate. Note: Don't use
5322 : : * RelationGetIndexExpressions()/RelationGetIndexPredicate(), because
5323 : : * those might run constant expressions evaluation, which needs a
5324 : : * snapshot, which we might not have here. (Also, it's probably more
5325 : : * sound to collect the bitmaps before any transformations that might
5326 : : * eliminate columns, but the practical impact of this is limited.)
5327 : : */
5328 : :
1903 peter@eisentraut.org 5329 : 12543 : datum = heap_getattr(indexDesc->rd_indextuple, Anum_pg_index_indexprs,
5330 : : GetPgIndexDescriptor(), &isnull);
5331 [ + + ]: 12543 : if (!isnull)
5332 : 64 : indexExpressions = stringToNode(TextDatumGetCString(datum));
5333 : : else
5334 : 12479 : indexExpressions = NULL;
5335 : :
5336 : 12543 : datum = heap_getattr(indexDesc->rd_indextuple, Anum_pg_index_indpred,
5337 : : GetPgIndexDescriptor(), &isnull);
5338 [ + + ]: 12543 : if (!isnull)
5339 : 57 : indexPredicate = stringToNode(TextDatumGetCString(datum));
5340 : : else
5341 : 12486 : indexPredicate = NULL;
5342 : :
5343 : : /* Can this index be referenced by a foreign key? */
5344 [ + + ]: 9870 : isKey = indexDesc->rd_index->indisunique &&
5345 [ + + + + ]: 22413 : indexExpressions == NULL &&
5346 : : indexPredicate == NULL;
5347 : :
5348 : : /* Is this a primary key? */
2642 peter_e@gmx.net 5349 : 12543 : isPK = (indexOid == relpkindex);
5350 : :
5351 : : /* Is this index the configured (or default) replica identity? */
3623 tgl@sss.pgh.pa.us 5352 : 12543 : isIDKey = (indexOid == relreplindex);
5353 : :
5354 : : /*
5355 : : * If the index is summarizing, it doesn't block HOT updates, but we
5356 : : * may still need to update it (if the attributes were modified). So
5357 : : * decide which bitmap we'll update in the following loop.
5358 : : */
391 tomas.vondra@postgre 5359 [ + + ]: 12543 : if (indexDesc->rd_indam->amsummarizing)
5360 : 28 : attrs = &summarizedattrs;
5361 : : else
5362 : 12515 : attrs = &hotblockingattrs;
5363 : :
5364 : : /* Collect simple attribute references */
1903 peter@eisentraut.org 5365 [ + + ]: 31725 : for (i = 0; i < indexDesc->rd_index->indnatts; i++)
5366 : : {
5367 : 19182 : int attrnum = indexDesc->rd_index->indkey.values[i];
5368 : :
5369 : : /*
5370 : : * Since we have covering indexes with non-key columns, we must
5371 : : * handle them accurately here. non-key columns must be added into
5372 : : * hotblockingattrs or summarizedattrs, since they are in index,
5373 : : * and update shouldn't miss them.
5374 : : *
5375 : : * Summarizing indexes do not block HOT, but do need to be updated
5376 : : * when the column value changes, thus require a separate
5377 : : * attribute bitmapset.
5378 : : *
5379 : : * Obviously, non-key columns couldn't be referenced by foreign
5380 : : * key or identity key. Hence we do not include them into
5381 : : * uindexattrs, pkindexattrs and idindexattrs bitmaps.
5382 : : */
6051 tgl@sss.pgh.pa.us 5383 [ + + ]: 19182 : if (attrnum != 0)
5384 : : {
391 tomas.vondra@postgre 5385 : 19118 : *attrs = bms_add_member(*attrs,
5386 : : attrnum - FirstLowInvalidHeapAttributeNumber);
5387 : :
1903 peter@eisentraut.org 5388 [ + + + + ]: 19118 : if (isKey && i < indexDesc->rd_index->indnkeyatts)
4099 alvherre@alvh.no-ip. 5389 : 14399 : uindexattrs = bms_add_member(uindexattrs,
5390 : : attrnum - FirstLowInvalidHeapAttributeNumber);
5391 : :
1903 peter@eisentraut.org 5392 [ + + + + ]: 19118 : if (isPK && i < indexDesc->rd_index->indnkeyatts)
2642 peter_e@gmx.net 5393 : 7734 : pkindexattrs = bms_add_member(pkindexattrs,
5394 : : attrnum - FirstLowInvalidHeapAttributeNumber);
5395 : :
1903 peter@eisentraut.org 5396 [ + + + + ]: 19118 : if (isIDKey && i < indexDesc->rd_index->indnkeyatts)
3623 tgl@sss.pgh.pa.us 5397 : 2882 : idindexattrs = bms_add_member(idindexattrs,
5398 : : attrnum - FirstLowInvalidHeapAttributeNumber);
5399 : : }
5400 : : }
5401 : :
5402 : : /* Collect all attributes used in expressions, too */
391 tomas.vondra@postgre 5403 : 12543 : pull_varattnos(indexExpressions, 1, attrs);
5404 : :
5405 : : /* Collect all attributes in the index predicate, too */
5406 : 12543 : pull_varattnos(indexPredicate, 1, attrs);
5407 : :
6051 tgl@sss.pgh.pa.us 5408 : 12543 : index_close(indexDesc, AccessShareLock);
5409 : : }
5410 : :
5411 : : /*
5412 : : * During one of the index_opens in the above loop, we might have received
5413 : : * a relcache flush event on this relcache entry, which might have been
5414 : : * signaling a change in the rel's index list. If so, we'd better start
5415 : : * over to ensure we deliver up-to-date attribute bitmaps.
5416 : : */
2624 5417 : 7358 : newindexoidlist = RelationGetIndexList(relation);
5418 [ + - ]: 7358 : if (equal(indexoidlist, newindexoidlist) &&
5419 [ + - ]: 7358 : relpkindex == relation->rd_pkindex &&
5420 [ + - ]: 7358 : relreplindex == relation->rd_replidindex)
5421 : : {
5422 : : /* Still the same index set, so proceed */
5423 : 7358 : list_free(newindexoidlist);
5424 : 7358 : list_free(indexoidlist);
5425 : : }
5426 : : else
5427 : : {
5428 : : /* Gotta do it over ... might as well not leak memory */
2624 tgl@sss.pgh.pa.us 5429 :UBC 0 : list_free(newindexoidlist);
5430 : 0 : list_free(indexoidlist);
5431 : 0 : bms_free(uindexattrs);
5432 : 0 : bms_free(pkindexattrs);
5433 : 0 : bms_free(idindexattrs);
391 tomas.vondra@postgre 5434 : 0 : bms_free(hotblockingattrs);
5435 : 0 : bms_free(summarizedattrs);
5436 : :
2624 tgl@sss.pgh.pa.us 5437 : 0 : goto restart;
5438 : : }
5439 : :
5440 : : /* Don't leak the old values of these bitmaps, if any */
391 tomas.vondra@postgre 5441 :CBC 7358 : relation->rd_attrsvalid = false;
3532 tgl@sss.pgh.pa.us 5442 : 7358 : bms_free(relation->rd_keyattr);
5443 : 7358 : relation->rd_keyattr = NULL;
2642 peter_e@gmx.net 5444 : 7358 : bms_free(relation->rd_pkattr);
5445 : 7358 : relation->rd_pkattr = NULL;
3532 tgl@sss.pgh.pa.us 5446 : 7358 : bms_free(relation->rd_idattr);
5447 : 7358 : relation->rd_idattr = NULL;
391 tomas.vondra@postgre 5448 : 7358 : bms_free(relation->rd_hotblockingattr);
5449 : 7358 : relation->rd_hotblockingattr = NULL;
5450 : 7358 : bms_free(relation->rd_summarizedattr);
5451 : 7358 : relation->rd_summarizedattr = NULL;
5452 : :
5453 : : /*
5454 : : * Now save copies of the bitmaps in the relcache entry. We intentionally
5455 : : * set rd_attrsvalid last, because that's the one that signals validity of
5456 : : * the values; if we run out of memory before making that copy, we won't
5457 : : * leave the relcache entry looking like the other ones are valid but
5458 : : * empty.
5459 : : */
6051 tgl@sss.pgh.pa.us 5460 : 7358 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
4099 alvherre@alvh.no-ip. 5461 : 7358 : relation->rd_keyattr = bms_copy(uindexattrs);
2642 peter_e@gmx.net 5462 : 7358 : relation->rd_pkattr = bms_copy(pkindexattrs);
3778 rhaas@postgresql.org 5463 : 7358 : relation->rd_idattr = bms_copy(idindexattrs);
391 tomas.vondra@postgre 5464 : 7358 : relation->rd_hotblockingattr = bms_copy(hotblockingattrs);
5465 : 7358 : relation->rd_summarizedattr = bms_copy(summarizedattrs);
5466 : 7358 : relation->rd_attrsvalid = true;
6051 tgl@sss.pgh.pa.us 5467 : 7358 : MemoryContextSwitchTo(oldcxt);
5468 : :
5469 : : /* We return our original working copy for caller to play with */
3631 bruce@momjian.us 5470 [ + + + + : 7358 : switch (attrKind)
- - ]
5471 : : {
3623 tgl@sss.pgh.pa.us 5472 : 497 : case INDEX_ATTR_BITMAP_KEY:
5473 : 497 : return uindexattrs;
2642 peter_e@gmx.net 5474 :GBC 1020 : case INDEX_ATTR_BITMAP_PRIMARY_KEY:
1910 tgl@sss.pgh.pa.us 5475 : 1020 : return pkindexattrs;
3623 tgl@sss.pgh.pa.us 5476 :CBC 501 : case INDEX_ATTR_BITMAP_IDENTITY_KEY:
5477 : 501 : return idindexattrs;
391 tomas.vondra@postgre 5478 : 5340 : case INDEX_ATTR_BITMAP_HOT_BLOCKING:
5479 : 5340 : return hotblockingattrs;
391 tomas.vondra@postgre 5480 :UBC 0 : case INDEX_ATTR_BITMAP_SUMMARIZED:
5481 : 0 : return summarizedattrs;
3778 rhaas@postgresql.org 5482 : 0 : default:
5483 [ # # ]: 0 : elog(ERROR, "unknown attrKind %u", attrKind);
5484 : : return NULL;
5485 : : }
5486 : : }
5487 : :
5488 : : /*
5489 : : * RelationGetIdentityKeyBitmap -- get a bitmap of replica identity attribute
5490 : : * numbers
5491 : : *
5492 : : * A bitmap of index attribute numbers for the configured replica identity
5493 : : * index is returned.
5494 : : *
5495 : : * See also comments of RelationGetIndexAttrBitmap().
5496 : : *
5497 : : * This is a special purpose function used during logical replication. Here,
5498 : : * unlike RelationGetIndexAttrBitmap(), we don't acquire a lock on the required
5499 : : * index as we build the cache entry using a historic snapshot and all the
5500 : : * later changes are absorbed while decoding WAL. Due to this reason, we don't
5501 : : * need to retry here in case of a change in the set of indexes.
5502 : : */
5503 : : Bitmapset *
1083 akapila@postgresql.o 5504 :CBC 335 : RelationGetIdentityKeyBitmap(Relation relation)
5505 : : {
5506 : 335 : Bitmapset *idindexattrs = NULL; /* columns in the replica identity */
5507 : : Relation indexDesc;
5508 : : int i;
5509 : : Oid replidindex;
5510 : : MemoryContext oldcxt;
5511 : :
5512 : : /* Quick exit if we already computed the result */
5513 [ + + ]: 335 : if (relation->rd_idattr != NULL)
5514 : 57 : return bms_copy(relation->rd_idattr);
5515 : :
5516 : : /* Fast path if definitely no indexes */
5517 [ + + ]: 278 : if (!RelationGetForm(relation)->relhasindex)
5518 : 72 : return NULL;
5519 : :
5520 : : /* Historic snapshot must be set. */
5521 [ - + ]: 206 : Assert(HistoricSnapshotActive());
5522 : :
1021 5523 : 206 : replidindex = RelationGetReplicaIndex(relation);
5524 : :
5525 : : /* Fall out if there is no replica identity index */
5526 [ + + ]: 206 : if (!OidIsValid(replidindex))
1030 5527 : 2 : return NULL;
5528 : :
5529 : : /* Look up the description for the replica identity index */
1021 5530 : 204 : indexDesc = RelationIdGetRelation(replidindex);
5531 : :
1030 5532 [ - + ]: 204 : if (!RelationIsValid(indexDesc))
1030 akapila@postgresql.o 5533 [ # # ]:UBC 0 : elog(ERROR, "could not open relation with OID %u",
5534 : : relation->rd_replidindex);
5535 : :
5536 : : /* Add referenced attributes to idindexattrs */
1083 akapila@postgresql.o 5537 [ + + ]:CBC 412 : for (i = 0; i < indexDesc->rd_index->indnatts; i++)
5538 : : {
5539 : 208 : int attrnum = indexDesc->rd_index->indkey.values[i];
5540 : :
5541 : : /*
5542 : : * We don't include non-key columns into idindexattrs bitmaps. See
5543 : : * RelationGetIndexAttrBitmap.
5544 : : */
5545 [ + - ]: 208 : if (attrnum != 0)
5546 : : {
5547 [ + + ]: 208 : if (i < indexDesc->rd_index->indnkeyatts)
5548 : 207 : idindexattrs = bms_add_member(idindexattrs,
5549 : : attrnum - FirstLowInvalidHeapAttributeNumber);
5550 : : }
5551 : : }
5552 : :
5553 : 204 : RelationClose(indexDesc);
5554 : :
5555 : : /* Don't leak the old values of these bitmaps, if any */
5556 : 204 : bms_free(relation->rd_idattr);
5557 : 204 : relation->rd_idattr = NULL;
5558 : :
5559 : : /* Now save copy of the bitmap in the relcache entry */
5560 : 204 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
5561 : 204 : relation->rd_idattr = bms_copy(idindexattrs);
5562 : 204 : MemoryContextSwitchTo(oldcxt);
5563 : :
5564 : : /* We return our original working copy for caller to play with */
5565 : 204 : return idindexattrs;
5566 : : }
5567 : :
5568 : : /*
5569 : : * RelationGetExclusionInfo -- get info about index's exclusion constraint
5570 : : *
5571 : : * This should be called only for an index that is known to have an associated
5572 : : * exclusion constraint or primary key/unique constraint using WITHOUT
5573 : : * OVERLAPS.
5574 : :
5575 : : * It returns arrays (palloc'd in caller's context) of the exclusion operator
5576 : : * OIDs, their underlying functions' OIDs, and their strategy numbers in the
5577 : : * index's opclasses. We cache all this information since it requires a fair
5578 : : * amount of work to get.
5579 : : */
5580 : : void
5242 tgl@sss.pgh.pa.us 5581 : 651 : RelationGetExclusionInfo(Relation indexRelation,
5582 : : Oid **operators,
5583 : : Oid **procs,
5584 : : uint16 **strategies)
5585 : : {
5586 : : int indnkeyatts;
5587 : : Oid *ops;
5588 : : Oid *funcs;
5589 : : uint16 *strats;
5590 : : Relation conrel;
5591 : : SysScanDesc conscan;
5592 : : ScanKeyData skey[1];
5593 : : HeapTuple htup;
5594 : : bool found;
5595 : : MemoryContext oldcxt;
5596 : : int i;
5597 : :
2199 teodor@sigaev.ru 5598 : 651 : indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRelation);
5599 : :
5600 : : /* Allocate result space in caller context */
5601 : 651 : *operators = ops = (Oid *) palloc(sizeof(Oid) * indnkeyatts);
5602 : 651 : *procs = funcs = (Oid *) palloc(sizeof(Oid) * indnkeyatts);
5603 : 651 : *strategies = strats = (uint16 *) palloc(sizeof(uint16) * indnkeyatts);
5604 : :
5605 : : /* Quick exit if we have the data cached already */
5242 tgl@sss.pgh.pa.us 5606 [ + + ]: 651 : if (indexRelation->rd_exclstrats != NULL)
5607 : : {
2199 teodor@sigaev.ru 5608 : 450 : memcpy(ops, indexRelation->rd_exclops, sizeof(Oid) * indnkeyatts);
5609 : 450 : memcpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * indnkeyatts);
5610 : 450 : memcpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * indnkeyatts);
5242 tgl@sss.pgh.pa.us 5611 : 450 : return;
5612 : : }
5613 : :
5614 : : /*
5615 : : * Search pg_constraint for the constraint associated with the index. To
5616 : : * make this not too painfully slow, we use the index on conrelid; that
5617 : : * will hold the parent relation's OID not the index's own OID.
5618 : : *
5619 : : * Note: if we wanted to rely on the constraint name matching the index's
5620 : : * name, we could just do a direct lookup using pg_constraint's unique
5621 : : * index. For the moment it doesn't seem worth requiring that.
5622 : : */
5623 : 201 : ScanKeyInit(&skey[0],
5624 : : Anum_pg_constraint_conrelid,
5625 : : BTEqualStrategyNumber, F_OIDEQ,
5626 : 201 : ObjectIdGetDatum(indexRelation->rd_index->indrelid));
5627 : :
1910 andres@anarazel.de 5628 : 201 : conrel = table_open(ConstraintRelationId, AccessShareLock);
2049 tgl@sss.pgh.pa.us 5629 : 201 : conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
5630 : : NULL, 1, skey);
5242 5631 : 201 : found = false;
5632 : :
5633 [ + + ]: 604 : while (HeapTupleIsValid(htup = systable_getnext(conscan)))
5634 : : {
5161 bruce@momjian.us 5635 : 403 : Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
5636 : : Datum val;
5637 : : bool isnull;
5638 : : ArrayType *arr;
5639 : : int nelem;
5640 : :
5641 : : /* We want the exclusion constraint owning the index */
81 peter@eisentraut.org 5642 [ + + ]:GNC 403 : if ((conform->contype != CONSTRAINT_EXCLUSION &&
40 5643 [ + + ]: 288 : !(conform->conperiod && (
5644 [ + + ]: 204 : conform->contype == CONSTRAINT_PRIMARY
5645 [ + + ]: 64 : || conform->contype == CONSTRAINT_UNIQUE))) ||
5242 tgl@sss.pgh.pa.us 5646 [ + + ]:CBC 267 : conform->conindid != RelationGetRelid(indexRelation))
5647 : 202 : continue;
5648 : :
5649 : : /* There should be only one */
5650 [ - + ]: 201 : if (found)
5242 tgl@sss.pgh.pa.us 5651 [ # # ]:UBC 0 : elog(ERROR, "unexpected exclusion constraint record found for rel %s",
5652 : : RelationGetRelationName(indexRelation));
5242 tgl@sss.pgh.pa.us 5653 :CBC 201 : found = true;
5654 : :
5655 : : /* Extract the operator OIDS from conexclop */
5656 : 201 : val = fastgetattr(htup,
5657 : : Anum_pg_constraint_conexclop,
5658 : : conrel->rd_att, &isnull);
5659 [ - + ]: 201 : if (isnull)
5242 tgl@sss.pgh.pa.us 5660 [ # # ]:UBC 0 : elog(ERROR, "null conexclop for rel %s",
5661 : : RelationGetRelationName(indexRelation));
5662 : :
5242 tgl@sss.pgh.pa.us 5663 :CBC 201 : arr = DatumGetArrayTypeP(val); /* ensure not toasted */
5664 : 201 : nelem = ARR_DIMS(arr)[0];
5665 [ + - + - ]: 201 : if (ARR_NDIM(arr) != 1 ||
2199 teodor@sigaev.ru 5666 : 201 : nelem != indnkeyatts ||
5242 tgl@sss.pgh.pa.us 5667 [ + - ]: 201 : ARR_HASNULL(arr) ||
5668 [ - + ]: 201 : ARR_ELEMTYPE(arr) != OIDOID)
5242 tgl@sss.pgh.pa.us 5669 [ # # ]:UBC 0 : elog(ERROR, "conexclop is not a 1-D Oid array");
5670 : :
2199 teodor@sigaev.ru 5671 [ - + ]:CBC 201 : memcpy(ops, ARR_DATA_PTR(arr), sizeof(Oid) * indnkeyatts);
5672 : : }
5673 : :
5242 tgl@sss.pgh.pa.us 5674 : 201 : systable_endscan(conscan);
1910 andres@anarazel.de 5675 : 201 : table_close(conrel, AccessShareLock);
5676 : :
5242 tgl@sss.pgh.pa.us 5677 [ - + ]: 201 : if (!found)
5242 tgl@sss.pgh.pa.us 5678 [ # # ]:UBC 0 : elog(ERROR, "exclusion constraint record missing for rel %s",
5679 : : RelationGetRelationName(indexRelation));
5680 : :
5681 : : /* We need the func OIDs and strategy numbers too */
2199 teodor@sigaev.ru 5682 [ + + ]:CBC 560 : for (i = 0; i < indnkeyatts; i++)
5683 : : {
5242 tgl@sss.pgh.pa.us 5684 : 359 : funcs[i] = get_opcode(ops[i]);
5685 : 718 : strats[i] = get_op_opfamily_strategy(ops[i],
5686 : 359 : indexRelation->rd_opfamily[i]);
5687 : : /* shouldn't fail, since it was checked at index creation */
5688 [ - + ]: 359 : if (strats[i] == InvalidStrategy)
5242 tgl@sss.pgh.pa.us 5689 [ # # ]:UBC 0 : elog(ERROR, "could not find strategy for operator %u in family %u",
5690 : : ops[i], indexRelation->rd_opfamily[i]);
5691 : : }
5692 : :
5693 : : /* Save a copy of the results in the relcache entry. */
5242 tgl@sss.pgh.pa.us 5694 :CBC 201 : oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt);
2199 teodor@sigaev.ru 5695 : 201 : indexRelation->rd_exclops = (Oid *) palloc(sizeof(Oid) * indnkeyatts);
5696 : 201 : indexRelation->rd_exclprocs = (Oid *) palloc(sizeof(Oid) * indnkeyatts);
5697 : 201 : indexRelation->rd_exclstrats = (uint16 *) palloc(sizeof(uint16) * indnkeyatts);
5698 : 201 : memcpy(indexRelation->rd_exclops, ops, sizeof(Oid) * indnkeyatts);
5699 : 201 : memcpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * indnkeyatts);
5700 : 201 : memcpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * indnkeyatts);
5242 tgl@sss.pgh.pa.us 5701 : 201 : MemoryContextSwitchTo(oldcxt);
5702 : : }
5703 : :
5704 : : /*
5705 : : * Get the publication information for the given relation.
5706 : : *
5707 : : * Traverse all the publications which the relation is in to get the
5708 : : * publication actions and validate the row filter expressions for such
5709 : : * publications if any. We consider the row filter expression as invalid if it
5710 : : * references any column which is not part of REPLICA IDENTITY.
5711 : : *
5712 : : * To avoid fetching the publication information repeatedly, we cache the
5713 : : * publication actions and row filter validation information.
5714 : : */
5715 : : void
782 akapila@postgresql.o 5716 : 85895 : RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
5717 : : {
5718 : : List *puboids;
5719 : : ListCell *lc;
5720 : : MemoryContext oldcxt;
5721 : : Oid schemaid;
5722 : 85895 : List *ancestors = NIL;
5723 : 85895 : Oid relid = RelationGetRelid(relation);
5724 : :
5725 : : /*
5726 : : * If not publishable, it publishes no actions. (pgoutput_change() will
5727 : : * ignore it.)
5728 : : */
1825 peter@eisentraut.org 5729 [ + + ]: 85895 : if (!is_publishable_relation(relation))
5730 : : {
782 akapila@postgresql.o 5731 : 2876 : memset(pubdesc, 0, sizeof(PublicationDesc));
5732 : 2876 : pubdesc->rf_valid_for_update = true;
5733 : 2876 : pubdesc->rf_valid_for_delete = true;
750 tomas.vondra@postgre 5734 : 2876 : pubdesc->cols_valid_for_update = true;
5735 : 2876 : pubdesc->cols_valid_for_delete = true;
782 akapila@postgresql.o 5736 : 2876 : return;
5737 : : }
5738 : :
5739 [ + + ]: 83019 : if (relation->rd_pubdesc)
5740 : : {
5741 : 79161 : memcpy(pubdesc, relation->rd_pubdesc, sizeof(PublicationDesc));
5742 : 79161 : return;
5743 : : }
5744 : :
5745 : 3858 : memset(pubdesc, 0, sizeof(PublicationDesc));
5746 : 3858 : pubdesc->rf_valid_for_update = true;
5747 : 3858 : pubdesc->rf_valid_for_delete = true;
750 tomas.vondra@postgre 5748 : 3858 : pubdesc->cols_valid_for_update = true;
5749 : 3858 : pubdesc->cols_valid_for_delete = true;
5750 : :
5751 : : /* Fetch the publication membership info. */
782 akapila@postgresql.o 5752 : 3858 : puboids = GetRelationPublications(relid);
900 5753 : 3858 : schemaid = RelationGetNamespace(relation);
738 tomas.vondra@postgre 5754 : 3858 : puboids = list_concat_unique_oid(puboids, GetSchemaPublications(schemaid));
5755 : :
1467 peter@eisentraut.org 5756 [ + + ]: 3858 : if (relation->rd_rel->relispartition)
5757 : : {
5758 : : /* Add publications that the ancestors are in too. */
782 akapila@postgresql.o 5759 : 882 : ancestors = get_partition_ancestors(relid);
5760 : :
1467 peter@eisentraut.org 5761 [ + - + + : 2056 : foreach(lc, ancestors)
+ + ]
5762 : : {
1431 tgl@sss.pgh.pa.us 5763 : 1174 : Oid ancestor = lfirst_oid(lc);
5764 : :
1467 peter@eisentraut.org 5765 : 1174 : puboids = list_concat_unique_oid(puboids,
5766 : 1174 : GetRelationPublications(ancestor));
900 akapila@postgresql.o 5767 : 1174 : schemaid = get_rel_namespace(ancestor);
5768 : 1174 : puboids = list_concat_unique_oid(puboids,
738 tomas.vondra@postgre 5769 : 1174 : GetSchemaPublications(schemaid));
5770 : : }
5771 : : }
5772 : 3858 : puboids = list_concat_unique_oid(puboids, GetAllTablesPublications());
5773 : :
2642 peter_e@gmx.net 5774 [ + + + + : 4180 : foreach(lc, puboids)
+ + ]
5775 : : {
5776 : 406 : Oid pubid = lfirst_oid(lc);
5777 : : HeapTuple tup;
5778 : : Form_pg_publication pubform;
5779 : :
5780 : 406 : tup = SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(pubid));
5781 : :
5782 [ - + ]: 406 : if (!HeapTupleIsValid(tup))
2642 peter_e@gmx.net 5783 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for publication %u", pubid);
5784 : :
2642 peter_e@gmx.net 5785 :CBC 406 : pubform = (Form_pg_publication) GETSTRUCT(tup);
5786 : :
782 akapila@postgresql.o 5787 : 406 : pubdesc->pubactions.pubinsert |= pubform->pubinsert;
5788 : 406 : pubdesc->pubactions.pubupdate |= pubform->pubupdate;
5789 : 406 : pubdesc->pubactions.pubdelete |= pubform->pubdelete;
5790 : 406 : pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
5791 : :
5792 : : /*
5793 : : * Check if all columns referenced in the filter expression are part
5794 : : * of the REPLICA IDENTITY index or not.
5795 : : *
5796 : : * If the publication is FOR ALL TABLES then it means the table has no
5797 : : * row filters and we can skip the validation.
5798 : : */
5799 [ + + ]: 406 : if (!pubform->puballtables &&
5800 [ + + + + : 664 : (pubform->pubupdate || pubform->pubdelete) &&
+ + ]
750 tomas.vondra@postgre 5801 : 331 : pub_rf_contains_invalid_column(pubid, relation, ancestors,
703 tgl@sss.pgh.pa.us 5802 : 331 : pubform->pubviaroot))
5803 : : {
782 akapila@postgresql.o 5804 [ + - ]: 30 : if (pubform->pubupdate)
5805 : 30 : pubdesc->rf_valid_for_update = false;
5806 [ + - ]: 30 : if (pubform->pubdelete)
5807 : 30 : pubdesc->rf_valid_for_delete = false;
5808 : : }
5809 : :
5810 : : /*
5811 : : * Check if all columns are part of the REPLICA IDENTITY index or not.
5812 : : *
5813 : : * If the publication is FOR ALL TABLES then it means the table has no
5814 : : * column list and we can skip the validation.
5815 : : */
750 tomas.vondra@postgre 5816 [ + + ]: 406 : if (!pubform->puballtables &&
5817 [ + + + + : 664 : (pubform->pubupdate || pubform->pubdelete) &&
+ + ]
5818 : 331 : pub_collist_contains_invalid_column(pubid, relation, ancestors,
703 tgl@sss.pgh.pa.us 5819 : 331 : pubform->pubviaroot))
5820 : : {
750 tomas.vondra@postgre 5821 [ + - ]: 54 : if (pubform->pubupdate)
5822 : 54 : pubdesc->cols_valid_for_update = false;
5823 [ + - ]: 54 : if (pubform->pubdelete)
5824 : 54 : pubdesc->cols_valid_for_delete = false;
5825 : : }
5826 : :
2642 peter_e@gmx.net 5827 : 406 : ReleaseSysCache(tup);
5828 : :
5829 : : /*
5830 : : * If we know everything is replicated and the row filter is invalid
5831 : : * for update and delete, there is no point to check for other
5832 : : * publications.
5833 : : */
782 akapila@postgresql.o 5834 [ + - + + ]: 406 : if (pubdesc->pubactions.pubinsert && pubdesc->pubactions.pubupdate &&
5835 [ + - + + ]: 403 : pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate &&
5836 [ + + + - ]: 397 : !pubdesc->rf_valid_for_update && !pubdesc->rf_valid_for_delete)
2642 peter_e@gmx.net 5837 : 30 : break;
5838 : :
5839 : : /*
5840 : : * If we know everything is replicated and the column list is invalid
5841 : : * for update and delete, there is no point to check for other
5842 : : * publications.
5843 : : */
750 tomas.vondra@postgre 5844 [ + - + + ]: 376 : if (pubdesc->pubactions.pubinsert && pubdesc->pubactions.pubupdate &&
5845 [ + - + + ]: 373 : pubdesc->pubactions.pubdelete && pubdesc->pubactions.pubtruncate &&
5846 [ + + + - ]: 367 : !pubdesc->cols_valid_for_update && !pubdesc->cols_valid_for_delete)
5847 : 54 : break;
5848 : : }
5849 : :
782 akapila@postgresql.o 5850 [ - + ]: 3858 : if (relation->rd_pubdesc)
5851 : : {
782 akapila@postgresql.o 5852 :UBC 0 : pfree(relation->rd_pubdesc);
5853 : 0 : relation->rd_pubdesc = NULL;
5854 : : }
5855 : :
5856 : : /* Now save copy of the descriptor in the relcache entry. */
2642 peter_e@gmx.net 5857 :CBC 3858 : oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
782 akapila@postgresql.o 5858 : 3858 : relation->rd_pubdesc = palloc(sizeof(PublicationDesc));
5859 : 3858 : memcpy(relation->rd_pubdesc, pubdesc, sizeof(PublicationDesc));
2642 peter_e@gmx.net 5860 : 3858 : MemoryContextSwitchTo(oldcxt);
5861 : : }
5862 : :
5863 : : static bytea **
1476 akorotkov@postgresql 5864 : 600398 : CopyIndexAttOptions(bytea **srcopts, int natts)
5865 : : {
5866 : 600398 : bytea **opts = palloc(sizeof(*opts) * natts);
5867 : :
5868 [ + + ]: 1693376 : for (int i = 0; i < natts; i++)
5869 : : {
5870 : 1092978 : bytea *opt = srcopts[i];
5871 : :
5872 [ + + ]: 1138996 : opts[i] = !opt ? NULL : (bytea *)
5873 : 46018 : DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
5874 : : }
5875 : :
5876 : 600398 : return opts;
5877 : : }
5878 : :
5879 : : /*
5880 : : * RelationGetIndexAttOptions
5881 : : * get AM/opclass-specific options for an index parsed into a binary form
5882 : : */
5883 : : bytea **
5884 : 1113619 : RelationGetIndexAttOptions(Relation relation, bool copy)
5885 : : {
5886 : : MemoryContext oldcxt;
5887 : 1113619 : bytea **opts = relation->rd_opcoptions;
5888 : 1113619 : Oid relid = RelationGetRelid(relation);
1431 tgl@sss.pgh.pa.us 5889 : 1113619 : int natts = RelationGetNumberOfAttributes(relation); /* XXX
5890 : : * IndexRelationGetNumberOfKeyAttributes */
5891 : : int i;
5892 : :
5893 : : /* Try to copy cached options. */
1476 akorotkov@postgresql 5894 [ + + ]: 1113619 : if (opts)
5895 [ + + ]: 819715 : return copy ? CopyIndexAttOptions(opts, natts) : opts;
5896 : :
5897 : : /* Get and parse opclass options. */
5898 : 293904 : opts = palloc0(sizeof(*opts) * natts);
5899 : :
5900 [ + + ]: 795872 : for (i = 0; i < natts; i++)
5901 : : {
5902 [ + + + - ]: 501971 : if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
5903 : : {
5904 : 473024 : Datum attoptions = get_attoptions(relid, i + 1);
5905 : :
5906 : 473024 : opts[i] = index_opclass_options(relation, i + 1, attoptions, false);
5907 : :
5908 [ + + ]: 473021 : if (attoptions != (Datum) 0)
5909 : 142 : pfree(DatumGetPointer(attoptions));
5910 : : }
5911 : : }
5912 : :
5913 : : /* Copy parsed options to the cache. */
5914 : 293901 : oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
5915 : 293901 : relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
5916 : 293901 : MemoryContextSwitchTo(oldcxt);
5917 : :
5918 [ - + ]: 293901 : if (copy)
1476 akorotkov@postgresql 5919 :UBC 0 : return opts;
5920 : :
1476 akorotkov@postgresql 5921 [ + + ]:CBC 795869 : for (i = 0; i < natts; i++)
5922 : : {
5923 [ + + ]: 501968 : if (opts[i])
5924 : 1024 : pfree(opts[i]);
5925 : : }
5926 : :
5927 : 293901 : pfree(opts);
5928 : :
5929 : 293901 : return relation->rd_opcoptions;
5930 : : }
5931 : :
5932 : : /*
5933 : : * Routines to support ereport() reports of relation-related errors
5934 : : *
5935 : : * These could have been put into elog.c, but it seems like a module layering
5936 : : * violation to have elog.c calling relcache or syscache stuff --- and we
5937 : : * definitely don't want elog.h including rel.h. So we put them here.
5938 : : */
5939 : :
5940 : : /*
5941 : : * errtable --- stores schema_name and table_name of a table
5942 : : * within the current errordata.
5943 : : */
5944 : : int
4093 tgl@sss.pgh.pa.us 5945 : 1624 : errtable(Relation rel)
5946 : : {
5947 : 1624 : err_generic_string(PG_DIAG_SCHEMA_NAME,
5948 : 1624 : get_namespace_name(RelationGetNamespace(rel)));
5949 : 1624 : err_generic_string(PG_DIAG_TABLE_NAME, RelationGetRelationName(rel));
5950 : :
3973 bruce@momjian.us 5951 : 1624 : return 0; /* return value does not matter */
5952 : : }
5953 : :
5954 : : /*
5955 : : * errtablecol --- stores schema_name, table_name and column_name
5956 : : * of a table column within the current errordata.
5957 : : *
5958 : : * The column is specified by attribute number --- for most callers, this is
5959 : : * easier and less error-prone than getting the column name for themselves.
5960 : : */
5961 : : int
4093 tgl@sss.pgh.pa.us 5962 : 199 : errtablecol(Relation rel, int attnum)
5963 : : {
5964 : 199 : TupleDesc reldesc = RelationGetDescr(rel);
5965 : : const char *colname;
5966 : :
5967 : : /* Use reldesc if it's a user attribute, else consult the catalogs */
5968 [ + - + - ]: 199 : if (attnum > 0 && attnum <= reldesc->natts)
2429 andres@anarazel.de 5969 : 199 : colname = NameStr(TupleDescAttr(reldesc, attnum - 1)->attname);
5970 : : else
2253 alvherre@alvh.no-ip. 5971 :UBC 0 : colname = get_attname(RelationGetRelid(rel), attnum, false);
5972 : :
4093 tgl@sss.pgh.pa.us 5973 :CBC 199 : return errtablecolname(rel, colname);
5974 : : }
5975 : :
5976 : : /*
5977 : : * errtablecolname --- stores schema_name, table_name and column_name
5978 : : * of a table column within the current errordata, where the column name is
5979 : : * given directly rather than extracted from the relation's catalog data.
5980 : : *
5981 : : * Don't use this directly unless errtablecol() is inconvenient for some
5982 : : * reason. This might possibly be needed during intermediate states in ALTER
5983 : : * TABLE, for instance.
5984 : : */
5985 : : int
5986 : 199 : errtablecolname(Relation rel, const char *colname)
5987 : : {
5988 : 199 : errtable(rel);
5989 : 199 : err_generic_string(PG_DIAG_COLUMN_NAME, colname);
5990 : :
3973 bruce@momjian.us 5991 : 199 : return 0; /* return value does not matter */
5992 : : }
5993 : :
5994 : : /*
5995 : : * errtableconstraint --- stores schema_name, table_name and constraint_name
5996 : : * of a table-related constraint within the current errordata.
5997 : : */
5998 : : int
4093 tgl@sss.pgh.pa.us 5999 : 1174 : errtableconstraint(Relation rel, const char *conname)
6000 : : {
6001 : 1174 : errtable(rel);
6002 : 1174 : err_generic_string(PG_DIAG_CONSTRAINT_NAME, conname);
6003 : :
3973 bruce@momjian.us 6004 : 1174 : return 0; /* return value does not matter */
6005 : : }
6006 : :
6007 : :
6008 : : /*
6009 : : * load_relcache_init_file, write_relcache_init_file
6010 : : *
6011 : : * In late 1992, we started regularly having databases with more than
6012 : : * a thousand classes in them. With this number of classes, it became
6013 : : * critical to do indexed lookups on the system catalogs.
6014 : : *
6015 : : * Bootstrapping these lookups is very hard. We want to be able to
6016 : : * use an index on pg_attribute, for example, but in order to do so,
6017 : : * we must have read pg_attribute for the attributes in the index,
6018 : : * which implies that we need to use the index.
6019 : : *
6020 : : * In order to get around the problem, we do the following:
6021 : : *
6022 : : * + When the database system is initialized (at initdb time), we
6023 : : * don't use indexes. We do sequential scans.
6024 : : *
6025 : : * + When the backend is started up in normal mode, we load an image
6026 : : * of the appropriate relation descriptors, in internal format,
6027 : : * from an initialization file in the data/base/... directory.
6028 : : *
6029 : : * + If the initialization file isn't there, then we create the
6030 : : * relation descriptors using sequential scans and write 'em to
6031 : : * the initialization file for use by subsequent backends.
6032 : : *
6033 : : * As of Postgres 9.0, there is one local initialization file in each
6034 : : * database, plus one shared initialization file for shared catalogs.
6035 : : *
6036 : : * We could dispense with the initialization files and just build the
6037 : : * critical reldescs the hard way on every backend startup, but that
6038 : : * slows down backend startup noticeably.
6039 : : *
6040 : : * We can in fact go further, and save more relcache entries than
6041 : : * just the ones that are absolutely critical; this allows us to speed
6042 : : * up backend startup by not having to build such entries the hard way.
6043 : : * Presently, all the catalog and index entries that are referred to
6044 : : * by catcaches are stored in the initialization files.
6045 : : *
6046 : : * The same mechanism that detects when catcache and relcache entries
6047 : : * need to be invalidated (due to catalog updates) also arranges to
6048 : : * unlink the initialization files when the contents may be out of date.
6049 : : * The files will then be rebuilt during the next backend startup.
6050 : : */
6051 : :
6052 : : /*
6053 : : * load_relcache_init_file -- attempt to load cache from the shared
6054 : : * or local cache init file
6055 : : *
6056 : : * If successful, return true and set criticalRelcachesBuilt or
6057 : : * criticalSharedRelcachesBuilt to true.
6058 : : * If not successful, return false.
6059 : : *
6060 : : * NOTE: we assume we are already switched into CacheMemoryContext.
6061 : : */
6062 : : static bool
5359 tgl@sss.pgh.pa.us 6063 : 29580 : load_relcache_init_file(bool shared)
6064 : : {
6065 : : FILE *fp;
6066 : : char initfilename[MAXPGPATH];
6067 : : Relation *rels;
6068 : : int relno,
6069 : : num_rels,
6070 : : max_rels,
6071 : : nailed_rels,
6072 : : nailed_indexes,
6073 : : magic;
6074 : : int i;
6075 : :
6076 [ + + ]: 29580 : if (shared)
6077 : 16365 : snprintf(initfilename, sizeof(initfilename), "global/%s",
6078 : : RELCACHE_INIT_FILENAME);
6079 : : else
6080 : 13215 : snprintf(initfilename, sizeof(initfilename), "%s/%s",
6081 : : DatabasePath, RELCACHE_INIT_FILENAME);
6082 : :
8090 6083 : 29580 : fp = AllocateFile(initfilename, PG_BINARY_R);
6084 [ + + ]: 29580 : if (fp == NULL)
6085 : 4303 : return false;
6086 : :
6087 : : /*
6088 : : * Read the index relcache entries from the file. Note we will not enter
6089 : : * any of them into the cache if the read fails partway through; this
6090 : : * helps to guard against broken init files.
6091 : : */
6092 : 25277 : max_rels = 100;
6093 : 25277 : rels = (Relation *) palloc(max_rels * sizeof(Relation));
6094 : 25277 : num_rels = 0;
6095 : 25277 : nailed_rels = nailed_indexes = 0;
6096 : :
6097 : : /* check for correct magic number (compatible version) */
7462 6098 [ - + ]: 25277 : if (fread(&magic, 1, sizeof(magic), fp) != sizeof(magic))
7462 tgl@sss.pgh.pa.us 6099 :UBC 0 : goto read_failed;
7462 tgl@sss.pgh.pa.us 6100 [ - + ]:CBC 25277 : if (magic != RELCACHE_INIT_FILEMAGIC)
7462 tgl@sss.pgh.pa.us 6101 :UBC 0 : goto read_failed;
6102 : :
7893 bruce@momjian.us 6103 :CBC 25277 : for (relno = 0;; relno++)
9716 6104 : 1619474 : {
6105 : : Size len;
6106 : : size_t nread;
6107 : : Relation rel;
6108 : : Form_pg_class relform;
6109 : : bool has_not_null;
6110 : :
6111 : : /* first read the relation descriptor length */
5341 tgl@sss.pgh.pa.us 6112 : 1644751 : nread = fread(&len, 1, sizeof(len), fp);
6113 [ + + ]: 1644751 : if (nread != sizeof(len))
6114 : : {
8090 6115 [ + - ]: 25276 : if (nread == 0)
6116 : 25276 : break; /* end of file */
8124 tgl@sss.pgh.pa.us 6117 :UBC 0 : goto read_failed;
6118 : : }
6119 : :
6120 : : /* safety check for incompatible relcache layout */
8226 tgl@sss.pgh.pa.us 6121 [ - + ]:CBC 1619475 : if (len != sizeof(RelationData))
8124 tgl@sss.pgh.pa.us 6122 :UBC 0 : goto read_failed;
6123 : :
6124 : : /* allocate another relcache header */
8090 tgl@sss.pgh.pa.us 6125 [ + + ]:CBC 1619475 : if (num_rels >= max_rels)
6126 : : {
6127 : 11815 : max_rels *= 2;
6128 : 11815 : rels = (Relation *) repalloc(rels, max_rels * sizeof(Relation));
6129 : : }
6130 : :
6131 : 1619475 : rel = rels[num_rels++] = (Relation) palloc(len);
6132 : :
6133 : : /* then, read the Relation structure */
5341 6134 [ - + ]: 1619475 : if (fread(rel, 1, len, fp) != len)
8124 tgl@sss.pgh.pa.us 6135 :UBC 0 : goto read_failed;
6136 : :
6137 : : /* next read the relation tuple form */
5341 tgl@sss.pgh.pa.us 6138 [ - + ]:CBC 1619475 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
8124 tgl@sss.pgh.pa.us 6139 :UBC 0 : goto read_failed;
6140 : :
9716 bruce@momjian.us 6141 :CBC 1619475 : relform = (Form_pg_class) palloc(len);
5341 tgl@sss.pgh.pa.us 6142 [ - + ]: 1619475 : if (fread(relform, 1, len, fp) != len)
8124 tgl@sss.pgh.pa.us 6143 :UBC 0 : goto read_failed;
6144 : :
8090 tgl@sss.pgh.pa.us 6145 :CBC 1619475 : rel->rd_rel = relform;
6146 : :
6147 : : /* initialize attribute tuple forms */
1972 andres@anarazel.de 6148 : 1619475 : rel->rd_att = CreateTemplateTupleDesc(relform->relnatts);
6512 tgl@sss.pgh.pa.us 6149 : 1619475 : rel->rd_att->tdrefcount = 1; /* mark as refcounted */
6150 : :
1377 6151 [ + + ]: 1619475 : rel->rd_att->tdtypeid = relform->reltype ? relform->reltype : RECORDOID;
6152 : 1619475 : rel->rd_att->tdtypmod = -1; /* just to be sure */
6153 : :
6154 : : /* next read all the attribute tuple form data entries */
7821 6155 : 1619475 : has_not_null = false;
9716 bruce@momjian.us 6156 [ + + ]: 9506672 : for (i = 0; i < relform->relnatts; i++)
6157 : : {
2429 andres@anarazel.de 6158 : 7887197 : Form_pg_attribute attr = TupleDescAttr(rel->rd_att, i);
6159 : :
5341 tgl@sss.pgh.pa.us 6160 [ - + ]: 7887197 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
8124 tgl@sss.pgh.pa.us 6161 :UBC 0 : goto read_failed;
5561 tgl@sss.pgh.pa.us 6162 [ - + ]:CBC 7887197 : if (len != ATTRIBUTE_FIXED_PART_SIZE)
6978 tgl@sss.pgh.pa.us 6163 :UBC 0 : goto read_failed;
2429 andres@anarazel.de 6164 [ - + ]:CBC 7887197 : if (fread(attr, 1, len, fp) != len)
8124 tgl@sss.pgh.pa.us 6165 :UBC 0 : goto read_failed;
6166 : :
2429 andres@anarazel.de 6167 :CBC 7887197 : has_not_null |= attr->attnotnull;
6168 : : }
6169 : :
6170 : : /* next read the access method specific field */
5341 tgl@sss.pgh.pa.us 6171 [ - + ]: 1619475 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
6496 bruce@momjian.us 6172 :UBC 0 : goto read_failed;
6496 bruce@momjian.us 6173 [ - + ]:CBC 1619475 : if (len > 0)
6174 : : {
6496 bruce@momjian.us 6175 :UBC 0 : rel->rd_options = palloc(len);
5341 tgl@sss.pgh.pa.us 6176 [ # # ]: 0 : if (fread(rel->rd_options, 1, len, fp) != len)
6496 bruce@momjian.us 6177 : 0 : goto read_failed;
6256 tgl@sss.pgh.pa.us 6178 [ # # ]: 0 : if (len != VARSIZE(rel->rd_options))
2489 6179 : 0 : goto read_failed; /* sanity check */
6180 : : }
6181 : : else
6182 : : {
6496 bruce@momjian.us 6183 :CBC 1619475 : rel->rd_options = NULL;
6184 : : }
6185 : :
6186 : : /* mark not-null status */
7821 tgl@sss.pgh.pa.us 6187 [ + + ]: 1619475 : if (has_not_null)
6188 : : {
6189 : 603939 : TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
6190 : :
6191 : 603939 : constr->has_not_null = true;
6192 : 603939 : rel->rd_att->constr = constr;
6193 : : }
6194 : :
6195 : : /*
6196 : : * If it's an index, there's more to do. Note we explicitly ignore
6197 : : * partitioned indexes here.
6198 : : */
8090 6199 [ + + ]: 1619475 : if (rel->rd_rel->relkind == RELKIND_INDEX)
6200 : : {
6201 : : MemoryContext indexcxt;
6202 : : Oid *opfamily;
6203 : : Oid *opcintype;
6204 : : RegProcedure *support;
6205 : : int nsupport;
6206 : : int16 *indoption;
6207 : : Oid *indcollation;
6208 : :
6209 : : /* Count nailed indexes to ensure we have 'em all */
6210 [ + + ]: 1015536 : if (rel->rd_isnailed)
6211 : 163474 : nailed_indexes++;
6212 : :
6213 : : /* read the pg_index tuple */
5341 6214 [ - + ]: 1015536 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
8090 tgl@sss.pgh.pa.us 6215 :UBC 0 : goto read_failed;
6216 : :
7627 tgl@sss.pgh.pa.us 6217 :CBC 1015536 : rel->rd_indextuple = (HeapTuple) palloc(len);
5341 6218 [ - + ]: 1015536 : if (fread(rel->rd_indextuple, 1, len, fp) != len)
8090 tgl@sss.pgh.pa.us 6219 :UBC 0 : goto read_failed;
6220 : :
6221 : : /* Fix up internal pointers in the tuple -- see heap_copytuple */
7627 tgl@sss.pgh.pa.us 6222 :CBC 1015536 : rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
6223 : 1015536 : rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
6224 : :
6225 : : /*
6226 : : * prepare index info context --- parameters should match
6227 : : * RelationInitIndexAccessInfo
6228 : : */
2210 6229 : 1015536 : indexcxt = AllocSetContextCreate(CacheMemoryContext,
6230 : : "index info",
6231 : : ALLOCSET_SMALL_SIZES);
8090 6232 : 1015536 : rel->rd_indexcxt = indexcxt;
2200 peter_e@gmx.net 6233 : 1015536 : MemoryContextCopyAndSetIdentifier(indexcxt,
6234 : : RelationGetRelationName(rel));
6235 : :
6236 : : /*
6237 : : * Now we can fetch the index AM's API struct. (We can't store
6238 : : * that in the init file, since it contains function pointers that
6239 : : * might vary across server executions. Fortunately, it should be
6240 : : * safe to call the amhandler even while bootstrapping indexes.)
6241 : : */
3010 tgl@sss.pgh.pa.us 6242 : 1015536 : InitIndexAmRoutine(rel);
6243 : :
6244 : : /* read the vector of opfamily OIDs */
5341 6245 [ - + ]: 1015536 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
6322 tgl@sss.pgh.pa.us 6246 :UBC 0 : goto read_failed;
6247 : :
6322 tgl@sss.pgh.pa.us 6248 :CBC 1015536 : opfamily = (Oid *) MemoryContextAlloc(indexcxt, len);
5341 6249 [ - + ]: 1015535 : if (fread(opfamily, 1, len, fp) != len)
6322 tgl@sss.pgh.pa.us 6250 :UBC 0 : goto read_failed;
6251 : :
6322 tgl@sss.pgh.pa.us 6252 :CBC 1015535 : rel->rd_opfamily = opfamily;
6253 : :
6254 : : /* read the vector of opcintype OIDs */
5341 6255 [ - + ]: 1015535 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
6322 tgl@sss.pgh.pa.us 6256 :UBC 0 : goto read_failed;
6257 : :
6322 tgl@sss.pgh.pa.us 6258 :CBC 1015535 : opcintype = (Oid *) MemoryContextAlloc(indexcxt, len);
5341 6259 [ - + ]: 1015535 : if (fread(opcintype, 1, len, fp) != len)
6322 tgl@sss.pgh.pa.us 6260 :UBC 0 : goto read_failed;
6261 : :
6322 tgl@sss.pgh.pa.us 6262 :CBC 1015535 : rel->rd_opcintype = opcintype;
6263 : :
6264 : : /* read the vector of support procedure OIDs */
5341 6265 [ - + ]: 1015535 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
8090 tgl@sss.pgh.pa.us 6266 :UBC 0 : goto read_failed;
8090 tgl@sss.pgh.pa.us 6267 :CBC 1015535 : support = (RegProcedure *) MemoryContextAlloc(indexcxt, len);
5341 6268 [ - + ]: 1015535 : if (fread(support, 1, len, fp) != len)
8090 tgl@sss.pgh.pa.us 6269 :UBC 0 : goto read_failed;
6270 : :
8090 tgl@sss.pgh.pa.us 6271 :CBC 1015535 : rel->rd_support = support;
6272 : :
6273 : : /* read the vector of collation OIDs */
4814 peter_e@gmx.net 6274 [ - + ]: 1015535 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
4814 peter_e@gmx.net 6275 :UBC 0 : goto read_failed;
6276 : :
4814 peter_e@gmx.net 6277 :CBC 1015535 : indcollation = (Oid *) MemoryContextAlloc(indexcxt, len);
6278 [ - + ]: 1015535 : if (fread(indcollation, 1, len, fp) != len)
4814 peter_e@gmx.net 6279 :UBC 0 : goto read_failed;
6280 : :
4814 peter_e@gmx.net 6281 :CBC 1015535 : rel->rd_indcollation = indcollation;
6282 : :
6283 : : /* read the vector of indoption values */
5341 tgl@sss.pgh.pa.us 6284 [ - + ]: 1015535 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
6305 tgl@sss.pgh.pa.us 6285 :UBC 0 : goto read_failed;
6286 : :
6305 tgl@sss.pgh.pa.us 6287 :CBC 1015535 : indoption = (int16 *) MemoryContextAlloc(indexcxt, len);
5341 6288 [ - + ]: 1015535 : if (fread(indoption, 1, len, fp) != len)
6305 tgl@sss.pgh.pa.us 6289 :UBC 0 : goto read_failed;
6290 : :
6305 tgl@sss.pgh.pa.us 6291 :CBC 1015535 : rel->rd_indoption = indoption;
6292 : :
6293 : : /* read the vector of opcoptions values */
1476 akorotkov@postgresql 6294 : 1015535 : rel->rd_opcoptions = (bytea **)
6295 : 1015535 : MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts);
6296 : :
6297 [ + + ]: 2692438 : for (i = 0; i < relform->relnatts; i++)
6298 : : {
6299 [ - + ]: 1676903 : if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
1476 akorotkov@postgresql 6300 :UBC 0 : goto read_failed;
6301 : :
1476 akorotkov@postgresql 6302 [ - + ]:CBC 1676903 : if (len > 0)
6303 : : {
1476 akorotkov@postgresql 6304 :UBC 0 : rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len);
6305 [ # # ]: 0 : if (fread(rel->rd_opcoptions[i], 1, len, fp) != len)
6306 : 0 : goto read_failed;
6307 : : }
6308 : : }
6309 : :
6310 : : /* set up zeroed fmgr-info vector */
1476 akorotkov@postgresql 6311 :CBC 1015535 : nsupport = relform->relnatts * rel->rd_indam->amsupport;
8090 tgl@sss.pgh.pa.us 6312 : 1015535 : rel->rd_supportinfo = (FmgrInfo *)
7462 6313 : 1015535 : MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
6314 : : }
6315 : : else
6316 : : {
6317 : : /* Count nailed rels to ensure we have 'em all */
8090 6318 [ + + ]: 603939 : if (rel->rd_isnailed)
6319 : 114566 : nailed_rels++;
6320 : :
6321 : : /* Load table AM data */
863 peter@eisentraut.org 6322 [ - + - - : 603939 : if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) || rel->rd_rel->relkind == RELKIND_SEQUENCE)
- - - - ]
1866 andres@anarazel.de 6323 : 603939 : RelationInitTableAccessMethod(rel);
6324 : :
8090 tgl@sss.pgh.pa.us 6325 [ - + ]: 603939 : Assert(rel->rd_index == NULL);
7627 6326 [ - + ]: 603939 : Assert(rel->rd_indextuple == NULL);
8090 6327 [ - + ]: 603939 : Assert(rel->rd_indexcxt == NULL);
1910 andres@anarazel.de 6328 [ - + ]: 603939 : Assert(rel->rd_indam == NULL);
6322 tgl@sss.pgh.pa.us 6329 [ - + ]: 603939 : Assert(rel->rd_opfamily == NULL);
6330 [ - + ]: 603939 : Assert(rel->rd_opcintype == NULL);
8090 6331 [ - + ]: 603939 : Assert(rel->rd_support == NULL);
6332 [ - + ]: 603939 : Assert(rel->rd_supportinfo == NULL);
6305 6333 [ - + ]: 603939 : Assert(rel->rd_indoption == NULL);
4814 peter_e@gmx.net 6334 [ - + ]: 603939 : Assert(rel->rd_indcollation == NULL);
1476 akorotkov@postgresql 6335 [ - + ]: 603939 : Assert(rel->rd_opcoptions == NULL);
6336 : : }
6337 : :
6338 : : /*
6339 : : * Rules and triggers are not saved (mainly because the internal
6340 : : * format is complex and subject to change). They must be rebuilt if
6341 : : * needed by RelationCacheInitializePhase3. This is not expected to
6342 : : * be a big performance hit since few system catalogs have such. Ditto
6343 : : * for RLS policy data, partition info, index expressions, predicates,
6344 : : * exclusion info, and FDW info.
6345 : : */
8090 tgl@sss.pgh.pa.us 6346 : 1619474 : rel->rd_rules = NULL;
6347 : 1619474 : rel->rd_rulescxt = NULL;
6348 : 1619474 : rel->trigdesc = NULL;
3439 sfrost@snowman.net 6349 : 1619474 : rel->rd_rsdesc = NULL;
2685 rhaas@postgresql.org 6350 : 1619474 : rel->rd_partkey = NULL;
1828 tgl@sss.pgh.pa.us 6351 : 1619474 : rel->rd_partkeycxt = NULL;
2685 rhaas@postgresql.org 6352 : 1619474 : rel->rd_partdesc = NULL;
1082 alvherre@alvh.no-ip. 6353 : 1619474 : rel->rd_partdesc_nodetached = NULL;
6354 : 1619474 : rel->rd_partdesc_nodetached_xmin = InvalidTransactionId;
1828 tgl@sss.pgh.pa.us 6355 : 1619474 : rel->rd_pdcxt = NULL;
1082 alvherre@alvh.no-ip. 6356 : 1619474 : rel->rd_pddcxt = NULL;
2685 rhaas@postgresql.org 6357 : 1619474 : rel->rd_partcheck = NIL;
1828 tgl@sss.pgh.pa.us 6358 : 1619474 : rel->rd_partcheckvalid = false;
6359 : 1619474 : rel->rd_partcheckcxt = NULL;
7627 6360 : 1619474 : rel->rd_indexprs = NIL;
6361 : 1619474 : rel->rd_indpred = NIL;
5242 6362 : 1619474 : rel->rd_exclops = NULL;
6363 : 1619474 : rel->rd_exclprocs = NULL;
6364 : 1619474 : rel->rd_exclstrats = NULL;
4057 6365 : 1619474 : rel->rd_fdwroutine = NULL;
6366 : :
6367 : : /*
6368 : : * Reset transient-state fields in the relcache entry
6369 : : */
7369 6370 : 1619474 : rel->rd_smgr = NULL;
8090 6371 [ + + ]: 1619474 : if (rel->rd_isnailed)
7211 6372 : 278040 : rel->rd_refcnt = 1;
6373 : : else
6374 : 1341434 : rel->rd_refcnt = 0;
1808 6375 : 1619474 : rel->rd_indexvalid = false;
8090 6376 : 1619474 : rel->rd_indexlist = NIL;
2642 peter_e@gmx.net 6377 : 1619474 : rel->rd_pkindex = InvalidOid;
3623 tgl@sss.pgh.pa.us 6378 : 1619474 : rel->rd_replidindex = InvalidOid;
391 tomas.vondra@postgre 6379 : 1619474 : rel->rd_attrsvalid = false;
3623 tgl@sss.pgh.pa.us 6380 : 1619474 : rel->rd_keyattr = NULL;
2642 peter_e@gmx.net 6381 : 1619474 : rel->rd_pkattr = NULL;
3623 tgl@sss.pgh.pa.us 6382 : 1619474 : rel->rd_idattr = NULL;
782 akapila@postgresql.o 6383 : 1619474 : rel->rd_pubdesc = NULL;
2578 alvherre@alvh.no-ip. 6384 : 1619474 : rel->rd_statvalid = false;
6385 : 1619474 : rel->rd_statlist = NIL;
1808 tgl@sss.pgh.pa.us 6386 : 1619474 : rel->rd_fkeyvalid = false;
6387 : 1619474 : rel->rd_fkeylist = NIL;
7085 6388 : 1619474 : rel->rd_createSubid = InvalidSubTransactionId;
648 rhaas@postgresql.org 6389 : 1619474 : rel->rd_newRelfilelocatorSubid = InvalidSubTransactionId;
6390 : 1619474 : rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
1471 noah@leadboat.com 6391 : 1619474 : rel->rd_droppedSubid = InvalidSubTransactionId;
6564 tgl@sss.pgh.pa.us 6392 : 1619474 : rel->rd_amcache = NULL;
654 peter@eisentraut.org 6393 : 1619474 : rel->pgstat_info = NULL;
6394 : :
6395 : : /*
6396 : : * Recompute lock and physical addressing info. This is needed in
6397 : : * case the pg_internal.init file was copied from some other database
6398 : : * by CREATE DATABASE.
6399 : : */
8090 tgl@sss.pgh.pa.us 6400 : 1619474 : RelationInitLockInfo(rel);
7240 6401 : 1619474 : RelationInitPhysicalAddr(rel);
6402 : : }
6403 : :
6404 : : /*
6405 : : * We reached the end of the init file without apparent problem. Did we
6406 : : * get the right number of nailed items? This is a useful crosscheck in
6407 : : * case the set of critical rels or indexes changes. However, that should
6408 : : * not happen in a normally-running system, so let's bleat if it does.
6409 : : *
6410 : : * For the shared init file, we're called before client authentication is
6411 : : * done, which means that elog(WARNING) will go only to the postmaster
6412 : : * log, where it's easily missed. To ensure that developers notice bad
6413 : : * values of NUM_CRITICAL_SHARED_RELS/NUM_CRITICAL_SHARED_INDEXES, we put
6414 : : * an Assert(false) there.
6415 : : */
5359 6416 [ + + ]: 25276 : if (shared)
6417 : : {
6418 [ + - - + ]: 13461 : if (nailed_rels != NUM_CRITICAL_SHARED_RELS ||
6419 : : nailed_indexes != NUM_CRITICAL_SHARED_INDEXES)
6420 : : {
3216 tgl@sss.pgh.pa.us 6421 [ # # ]:UBC 0 : elog(WARNING, "found %d nailed shared rels and %d nailed shared indexes in init file, but expected %d and %d respectively",
6422 : : nailed_rels, nailed_indexes,
6423 : : NUM_CRITICAL_SHARED_RELS, NUM_CRITICAL_SHARED_INDEXES);
6424 : : /* Make sure we get developers' attention about this */
3077 6425 : 0 : Assert(false);
6426 : : /* In production builds, recover by bootstrapping the relcache */
6427 : : goto read_failed;
6428 : : }
6429 : : }
6430 : : else
6431 : : {
5359 tgl@sss.pgh.pa.us 6432 [ + - - + ]:CBC 11815 : if (nailed_rels != NUM_CRITICAL_LOCAL_RELS ||
6433 : : nailed_indexes != NUM_CRITICAL_LOCAL_INDEXES)
6434 : : {
3216 tgl@sss.pgh.pa.us 6435 [ # # ]:UBC 0 : elog(WARNING, "found %d nailed rels and %d nailed indexes in init file, but expected %d and %d respectively",
6436 : : nailed_rels, nailed_indexes,
6437 : : NUM_CRITICAL_LOCAL_RELS, NUM_CRITICAL_LOCAL_INDEXES);
6438 : : /* We don't need an Assert() in this case */
5359 6439 : 0 : goto read_failed;
6440 : : }
6441 : : }
6442 : :
6443 : : /*
6444 : : * OK, all appears well.
6445 : : *
6446 : : * Now insert all the new relcache entries into the cache.
6447 : : */
8090 tgl@sss.pgh.pa.us 6448 [ + + ]:CBC 1644698 : for (relno = 0; relno < num_rels; relno++)
6449 : : {
3619 6450 [ - + ]: 1619422 : RelationCacheInsert(rels[relno], false);
6451 : : }
6452 : :
8090 6453 : 25276 : pfree(rels);
6454 : 25276 : FreeFile(fp);
6455 : :
5359 6456 [ + + ]: 25276 : if (shared)
6457 : 13461 : criticalSharedRelcachesBuilt = true;
6458 : : else
6459 : 11815 : criticalRelcachesBuilt = true;
8090 6460 : 25276 : return true;
6461 : :
6462 : : /*
6463 : : * init file is broken, so do it the hard way. We don't bother trying to
6464 : : * free the clutter we just allocated; it's not in the relcache so it
6465 : : * won't hurt.
6466 : : */
8124 tgl@sss.pgh.pa.us 6467 :UBC 0 : read_failed:
8090 6468 : 0 : pfree(rels);
6469 : 0 : FreeFile(fp);
6470 : :
6471 : 0 : return false;
6472 : : }
6473 : :
6474 : : /*
6475 : : * Write out a new initialization file with the current contents
6476 : : * of the relcache (either shared rels or local rels, as indicated).
6477 : : */
6478 : : static void
5359 tgl@sss.pgh.pa.us 6479 :CBC 2890 : write_relcache_init_file(bool shared)
6480 : : {
6481 : : FILE *fp;
6482 : : char tempfilename[MAXPGPATH];
6483 : : char finalfilename[MAXPGPATH];
6484 : : int magic;
6485 : : HASH_SEQ_STATUS status;
6486 : : RelIdCacheEnt *idhentry;
6487 : : int i;
6488 : :
6489 : : /*
6490 : : * If we have already received any relcache inval events, there's no
6491 : : * chance of succeeding so we may as well skip the whole thing.
6492 : : */
3234 6493 [ + + ]: 2890 : if (relcacheInvalsReceived != 0L)
6494 : 26 : return;
6495 : :
6496 : : /*
6497 : : * We must write a temporary file and rename it into place. Otherwise,
6498 : : * another backend starting at about the same time might crash trying to
6499 : : * read the partially-complete file.
6500 : : */
5359 6501 [ + + ]: 2864 : if (shared)
6502 : : {
6503 : 1433 : snprintf(tempfilename, sizeof(tempfilename), "global/%s.%d",
6504 : : RELCACHE_INIT_FILENAME, MyProcPid);
6505 : 1433 : snprintf(finalfilename, sizeof(finalfilename), "global/%s",
6506 : : RELCACHE_INIT_FILENAME);
6507 : : }
6508 : : else
6509 : : {
6510 : 1431 : snprintf(tempfilename, sizeof(tempfilename), "%s/%s.%d",
6511 : : DatabasePath, RELCACHE_INIT_FILENAME, MyProcPid);
6512 : 1431 : snprintf(finalfilename, sizeof(finalfilename), "%s/%s",
6513 : : DatabasePath, RELCACHE_INIT_FILENAME);
6514 : : }
6515 : :
8090 6516 : 2864 : unlink(tempfilename); /* in case it exists w/wrong permissions */
6517 : :
6518 : 2864 : fp = AllocateFile(tempfilename, PG_BINARY_W);
6519 [ - + ]: 2864 : if (fp == NULL)
6520 : : {
6521 : : /*
6522 : : * We used to consider this a fatal error, but we might as well
6523 : : * continue with backend startup ...
6524 : : */
7569 tgl@sss.pgh.pa.us 6525 [ # # ]:UBC 0 : ereport(WARNING,
6526 : : (errcode_for_file_access(),
6527 : : errmsg("could not create relation-cache initialization file \"%s\": %m",
6528 : : tempfilename),
6529 : : errdetail("Continuing anyway, but there's something wrong.")));
8700 6530 : 0 : return;
6531 : : }
6532 : :
6533 : : /*
6534 : : * Write a magic number to serve as a file version identifier. We can
6535 : : * change the magic number whenever the relcache layout changes.
6536 : : */
7462 tgl@sss.pgh.pa.us 6537 :CBC 2864 : magic = RELCACHE_INIT_FILEMAGIC;
6538 [ - + ]: 2864 : if (fwrite(&magic, 1, sizeof(magic), fp) != sizeof(magic))
11 dgustafsson@postgres 6539 [ # # ]:UNC 0 : ereport(FATAL,
6540 : : errcode_for_file_access(),
6541 : : errmsg_internal("could not write init file: %m"));
6542 : :
6543 : : /*
6544 : : * Write all the appropriate reldescs (in no particular order).
6545 : : */
8055 tgl@sss.pgh.pa.us 6546 :CBC 2864 : hash_seq_init(&status, RelationIdCache);
6547 : :
6548 [ + + ]: 386640 : while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
6549 : : {
6550 : 383776 : Relation rel = idhentry->reldesc;
8090 6551 : 383776 : Form_pg_class relform = rel->rd_rel;
6552 : :
6553 : : /* ignore if not correct group */
5359 6554 [ + + ]: 383776 : if (relform->relisshared != shared)
6555 : 191978 : continue;
6556 : :
6557 : : /*
6558 : : * Ignore if not supposed to be in init file. We can allow any shared
6559 : : * relation that's been loaded so far to be in the shared init file,
6560 : : * but unshared relations must be ones that should be in the local
6561 : : * file per RelationIdIsInInitFile. (Note: if you want to change the
6562 : : * criterion for rels to be kept in the init file, see also inval.c.
6563 : : * The reason for filtering here is to be sure that we don't put
6564 : : * anything into the local init file for which a relcache inval would
6565 : : * not cause invalidation of that init file.)
6566 : : */
3216 6567 [ + + - + ]: 191798 : if (!shared && !RelationIdIsInInitFile(RelationGetRelid(rel)))
6568 : : {
6569 : : /* Nailed rels had better get stored. */
3216 tgl@sss.pgh.pa.us 6570 [ # # ]:UBC 0 : Assert(!rel->rd_isnailed);
3234 6571 : 0 : continue;
6572 : : }
6573 : :
6574 : : /* first write the relcache entry proper */
6496 bruce@momjian.us 6575 :CBC 191798 : write_item(rel, sizeof(RelationData), fp);
6576 : :
6577 : : /* next write the relation tuple form */
6578 : 191798 : write_item(relform, CLASS_TUPLE_SIZE, fp);
6579 : :
6580 : : /* next, do all the attribute tuple form data entries */
9716 6581 [ + + ]: 1129283 : for (i = 0; i < relform->relnatts; i++)
6582 : : {
2429 andres@anarazel.de 6583 : 937485 : write_item(TupleDescAttr(rel->rd_att, i),
6584 : : ATTRIBUTE_FIXED_PART_SIZE, fp);
6585 : : }
6586 : :
6587 : : /* next, do the access method specific field */
6496 bruce@momjian.us 6588 :UBC 0 : write_item(rel->rd_options,
6256 tgl@sss.pgh.pa.us 6589 [ - + ]:CBC 191798 : (rel->rd_options ? VARSIZE(rel->rd_options) : 0),
6590 : : fp);
6591 : :
6592 : : /*
6593 : : * If it's an index, there's more to do. Note we explicitly ignore
6594 : : * partitioned indexes here.
6595 : : */
8090 6596 [ + + ]: 191798 : if (rel->rd_rel->relkind == RELKIND_INDEX)
6597 : : {
6598 : : /* write the pg_index tuple */
6599 : : /* we assume this was created by heap_copytuple! */
6496 bruce@momjian.us 6600 : 120232 : write_item(rel->rd_indextuple,
6495 tgl@sss.pgh.pa.us 6601 : 120232 : HEAPTUPLESIZE + rel->rd_indextuple->t_len,
6602 : : fp);
6603 : :
6604 : : /* write the vector of opfamily OIDs */
6322 6605 : 120232 : write_item(rel->rd_opfamily,
6606 : 120232 : relform->relnatts * sizeof(Oid),
6607 : : fp);
6608 : :
6609 : : /* write the vector of opcintype OIDs */
6610 : 120232 : write_item(rel->rd_opcintype,
6611 : 120232 : relform->relnatts * sizeof(Oid),
6612 : : fp);
6613 : :
6614 : : /* write the vector of support procedure OIDs */
6495 6615 : 120232 : write_item(rel->rd_support,
1476 akorotkov@postgresql 6616 : 120232 : relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
6617 : : fp);
6618 : :
6619 : : /* write the vector of collation OIDs */
4814 peter_e@gmx.net 6620 : 120232 : write_item(rel->rd_indcollation,
6621 : 120232 : relform->relnatts * sizeof(Oid),
6622 : : fp);
6623 : :
6624 : : /* write the vector of indoption values */
6305 tgl@sss.pgh.pa.us 6625 : 120232 : write_item(rel->rd_indoption,
6626 : 120232 : relform->relnatts * sizeof(int16),
6627 : : fp);
6628 : :
1476 akorotkov@postgresql 6629 [ - + ]: 120232 : Assert(rel->rd_opcoptions);
6630 : :
6631 : : /* write the vector of opcoptions values */
6632 [ + + ]: 319183 : for (i = 0; i < relform->relnatts; i++)
6633 : : {
6634 : 198951 : bytea *opt = rel->rd_opcoptions[i];
6635 : :
6636 [ - + ]: 198951 : write_item(opt, opt ? VARSIZE(opt) : 0, fp);
6637 : : }
6638 : : }
6639 : : }
6640 : :
7384 tgl@sss.pgh.pa.us 6641 [ - + ]: 2864 : if (FreeFile(fp))
11 dgustafsson@postgres 6642 [ # # ]:UNC 0 : ereport(FATAL,
6643 : : errcode_for_file_access(),
6644 : : errmsg_internal("could not write init file: %m"));
6645 : :
6646 : : /*
6647 : : * Now we have to check whether the data we've so painstakingly
6648 : : * accumulated is already obsolete due to someone else's just-committed
6649 : : * catalog changes. If so, we just delete the temp file and leave it to
6650 : : * the next backend to try again. (Our own relcache entries will be
6651 : : * updated by SI message processing, but we can't be sure whether what we
6652 : : * wrote out was up-to-date.)
6653 : : *
6654 : : * This mustn't run concurrently with the code that unlinks an init file
6655 : : * and sends SI messages, so grab a serialization lock for the duration.
6656 : : */
8090 tgl@sss.pgh.pa.us 6657 :CBC 2864 : LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);
6658 : :
6659 : : /* Make sure we have seen all incoming SI messages */
6660 : 2864 : AcceptInvalidationMessages();
6661 : :
6662 : : /*
6663 : : * If we have received any SI relcache invals since backend start, assume
6664 : : * we may have written out-of-date data.
6665 : : */
6666 [ + + ]: 2864 : if (relcacheInvalsReceived == 0L)
6667 : : {
6668 : : /*
6669 : : * OK, rename the temp file to its final name, deleting any
6670 : : * previously-existing init file.
6671 : : *
6672 : : * Note: a failure here is possible under Cygwin, if some other
6673 : : * backend is holding open an unlinked-but-not-yet-gone init file. So
6674 : : * treat this as a noncritical failure; just remove the useless temp
6675 : : * file on failure.
6676 : : */
7063 6677 [ - + ]: 2861 : if (rename(tempfilename, finalfilename) < 0)
7063 tgl@sss.pgh.pa.us 6678 :UBC 0 : unlink(tempfilename);
6679 : : }
6680 : : else
6681 : : {
6682 : : /* Delete the already-obsolete temp file */
8125 tgl@sss.pgh.pa.us 6683 :CBC 3 : unlink(tempfilename);
6684 : : }
6685 : :
7063 6686 : 2864 : LWLockRelease(RelCacheInitLock);
6687 : : }
6688 : :
6689 : : /* write a chunk of data preceded by its length */
6690 : : static void
6495 6691 : 2433222 : write_item(const void *data, Size len, FILE *fp)
6692 : : {
6693 [ - + ]: 2433222 : if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
11 dgustafsson@postgres 6694 [ # # ]:UNC 0 : ereport(FATAL,
6695 : : errcode_for_file_access(),
6696 : : errmsg_internal("could not write init file: %m"));
753 andres@anarazel.de 6697 [ + + - + ]:CBC 2433222 : if (len > 0 && fwrite(data, 1, len, fp) != len)
11 dgustafsson@postgres 6698 [ # # ]:UNC 0 : ereport(FATAL,
6699 : : errcode_for_file_access(),
6700 : : errmsg_internal("could not write init file: %m"));
6495 tgl@sss.pgh.pa.us 6701 :CBC 2433222 : }
6702 : :
6703 : : /*
6704 : : * Determine whether a given relation (identified by OID) is one of the ones
6705 : : * we should store in a relcache init file.
6706 : : *
6707 : : * We must cache all nailed rels, and for efficiency we should cache every rel
6708 : : * that supports a syscache. The former set is almost but not quite a subset
6709 : : * of the latter. The special cases are relations where
6710 : : * RelationCacheInitializePhase2/3 chooses to nail for efficiency reasons, but
6711 : : * which do not support any syscache.
6712 : : */
6713 : : bool
3216 6714 : 989064 : RelationIdIsInInitFile(Oid relationId)
6715 : : {
2133 andres@anarazel.de 6716 [ + + + + ]: 989064 : if (relationId == SharedSecLabelRelationId ||
6717 [ + + ]: 987204 : relationId == TriggerRelidNameIndexId ||
6718 [ + + ]: 987161 : relationId == DatabaseNameIndexId ||
6719 : : relationId == SharedSecLabelObjectIndexId)
6720 : : {
6721 : : /*
6722 : : * If this Assert fails, we don't need the applicable special case
6723 : : * anymore.
6724 : : */
3216 tgl@sss.pgh.pa.us 6725 [ - + ]: 1958 : Assert(!RelationSupportsSysCache(relationId));
6726 : 1958 : return true;
6727 : : }
6728 : 987106 : return RelationSupportsSysCache(relationId);
6729 : : }
6730 : :
6731 : : /*
6732 : : * Invalidate (remove) the init file during commit of a transaction that
6733 : : * changed one or more of the relation cache entries that are kept in the
6734 : : * local init file.
6735 : : *
6736 : : * To be safe against concurrent inspection or rewriting of the init file,
6737 : : * we must take RelCacheInitLock, then remove the old init file, then send
6738 : : * the SI messages that include relcache inval for such relations, and then
6739 : : * release RelCacheInitLock. This serializes the whole affair against
6740 : : * write_relcache_init_file, so that we can be sure that any other process
6741 : : * that's concurrently trying to create a new init file won't move an
6742 : : * already-stale version into place after we unlink. Also, because we unlink
6743 : : * before sending the SI messages, a backend that's currently starting cannot
6744 : : * read the now-obsolete init file and then miss the SI messages that will
6745 : : * force it to update its relcache entries. (This works because the backend
6746 : : * startup sequence gets into the sinval array before trying to load the init
6747 : : * file.)
6748 : : *
6749 : : * We take the lock and do the unlink in RelationCacheInitFilePreInvalidate,
6750 : : * then release the lock in RelationCacheInitFilePostInvalidate. Caller must
6751 : : * send any pending SI messages between those calls.
6752 : : */
6753 : : void
4625 6754 : 15650 : RelationCacheInitFilePreInvalidate(void)
6755 : : {
6756 : : char localinitfname[MAXPGPATH];
6757 : : char sharedinitfname[MAXPGPATH];
6758 : :
2133 andres@anarazel.de 6759 [ + - ]: 15650 : if (DatabasePath)
6760 : 15650 : snprintf(localinitfname, sizeof(localinitfname), "%s/%s",
6761 : : DatabasePath, RELCACHE_INIT_FILENAME);
6762 : 15650 : snprintf(sharedinitfname, sizeof(sharedinitfname), "global/%s",
6763 : : RELCACHE_INIT_FILENAME);
6764 : :
4625 tgl@sss.pgh.pa.us 6765 : 15650 : LWLockAcquire(RelCacheInitLock, LW_EXCLUSIVE);
6766 : :
6767 : : /*
6768 : : * The files might not be there if no backend has been started since the
6769 : : * last removal. But complain about failures other than ENOENT with
6770 : : * ERROR. Fortunately, it's not too late to abort the transaction if we
6771 : : * can't get rid of the would-be-obsolete init file.
6772 : : */
2133 andres@anarazel.de 6773 [ + - ]: 15650 : if (DatabasePath)
6774 : 15650 : unlink_initfile(localinitfname, ERROR);
6775 : 15650 : unlink_initfile(sharedinitfname, ERROR);
10141 scrappy@hub.org 6776 : 15650 : }
6777 : :
6778 : : void
4625 tgl@sss.pgh.pa.us 6779 : 15650 : RelationCacheInitFilePostInvalidate(void)
6780 : : {
6781 : 15650 : LWLockRelease(RelCacheInitLock);
6782 : 15650 : }
6783 : :
6784 : : /*
6785 : : * Remove the init files during postmaster startup.
6786 : : *
6787 : : * We used to keep the init files across restarts, but that is unsafe in PITR
6788 : : * scenarios, and even in simple crash-recovery cases there are windows for
6789 : : * the init files to become out-of-sync with the database. So now we just
6790 : : * remove them during startup and expect the first backend launch to rebuild
6791 : : * them. Of course, this has to happen in each database of the cluster.
6792 : : */
6793 : : void
5359 6794 : 823 : RelationCacheInitFileRemove(void)
6795 : : {
6796 : 823 : const char *tblspcdir = "pg_tblspc";
6797 : : DIR *dir;
6798 : : struct dirent *de;
6799 : : char path[MAXPGPATH + 10 + sizeof(TABLESPACE_VERSION_DIRECTORY)];
6800 : :
6801 : 823 : snprintf(path, sizeof(path), "global/%s",
6802 : : RELCACHE_INIT_FILENAME);
2133 andres@anarazel.de 6803 : 823 : unlink_initfile(path, LOG);
6804 : :
6805 : : /* Scan everything in the default tablespace */
5359 tgl@sss.pgh.pa.us 6806 : 823 : RelationCacheInitFileRemoveInDir("base");
6807 : :
6808 : : /* Scan the tablespace link directory to find non-default tablespaces */
6809 : 823 : dir = AllocateDir(tblspcdir);
6810 : :
2323 6811 [ + + ]: 3356 : while ((de = ReadDirExtended(dir, tblspcdir, LOG)) != NULL)
6812 : : {
5359 6813 [ + + ]: 1710 : if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
6814 : : {
6815 : : /* Scan the tablespace dir for per-database dirs */
5206 bruce@momjian.us 6816 : 64 : snprintf(path, sizeof(path), "%s/%s/%s",
6817 : 64 : tblspcdir, de->d_name, TABLESPACE_VERSION_DIRECTORY);
5359 tgl@sss.pgh.pa.us 6818 : 64 : RelationCacheInitFileRemoveInDir(path);
6819 : : }
6820 : : }
6821 : :
6822 : 823 : FreeDir(dir);
6823 : 823 : }
6824 : :
6825 : : /* Process one per-tablespace directory for RelationCacheInitFileRemove */
6826 : : static void
6827 : 887 : RelationCacheInitFileRemoveInDir(const char *tblspcpath)
6828 : : {
6829 : : DIR *dir;
6830 : : struct dirent *de;
6831 : : char initfilename[MAXPGPATH * 2];
6832 : :
6833 : : /* Scan the tablespace directory to find per-database directories */
6834 : 887 : dir = AllocateDir(tblspcpath);
6835 : :
2323 6836 [ + + ]: 6239 : while ((de = ReadDirExtended(dir, tblspcpath, LOG)) != NULL)
6837 : : {
5359 6838 [ + + ]: 4465 : if (strspn(de->d_name, "0123456789") == strlen(de->d_name))
6839 : : {
6840 : : /* Try to remove the init file in each database */
6841 : 2619 : snprintf(initfilename, sizeof(initfilename), "%s/%s/%s",
6842 : 2619 : tblspcpath, de->d_name, RELCACHE_INIT_FILENAME);
2133 andres@anarazel.de 6843 : 2619 : unlink_initfile(initfilename, LOG);
6844 : : }
6845 : : }
6846 : :
5359 tgl@sss.pgh.pa.us 6847 : 887 : FreeDir(dir);
6848 : 887 : }
6849 : :
6850 : : static void
2133 andres@anarazel.de 6851 : 34742 : unlink_initfile(const char *initfilename, int elevel)
6852 : : {
5359 tgl@sss.pgh.pa.us 6853 [ + + ]: 34742 : if (unlink(initfilename) < 0)
6854 : : {
6855 : : /* It might not be there, but log any error other than ENOENT */
6856 [ - + ]: 33493 : if (errno != ENOENT)
2133 andres@anarazel.de 6857 [ # # ]:UBC 0 : ereport(elevel,
6858 : : (errcode_for_file_access(),
6859 : : errmsg("could not remove cache file \"%s\": %m",
6860 : : initfilename)));
6861 : : }
6370 tgl@sss.pgh.pa.us 6862 :CBC 34742 : }
6863 : :
6864 : : /*
6865 : : * ResourceOwner callbacks
6866 : : */
6867 : : static char *
158 heikki.linnakangas@i 6868 :UNC 0 : ResOwnerPrintRelCache(Datum res)
6869 : : {
6870 : 0 : Relation rel = (Relation) DatumGetPointer(res);
6871 : :
6872 : 0 : return psprintf("relation \"%s\"", RelationGetRelationName(rel));
6873 : : }
6874 : :
6875 : : static void
158 heikki.linnakangas@i 6876 :GNC 17625 : ResOwnerReleaseRelation(Datum res)
6877 : : {
6878 : 17625 : Relation rel = (Relation) DatumGetPointer(res);
6879 : :
6880 : : /*
6881 : : * This reference has already been removed from the resource owner, so
6882 : : * just decrement reference count without calling
6883 : : * ResourceOwnerForgetRelationRef.
6884 : : */
6885 [ - + ]: 17625 : Assert(rel->rd_refcnt > 0);
6886 : 17625 : rel->rd_refcnt -= 1;
6887 : :
6888 : 17625 : RelationCloseCleanup((Relation) res);
6889 : 17625 : }
|