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