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