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