/* * Auto-stats employs this sub-routine to issue an analyze on a specific relation. */ static void autostats_issue_analyze(Oid relationOid) { VacuumStmt *analyzeStmt = NULL; RangeVar *relation = NULL; /* * If this user does not own the table, then auto-stats will not issue the * analyze. */ if (!(pg_class_ownercheck(relationOid, GetUserId()) || (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !IsSharedRelation(relationOid)))) { elog(DEBUG3, "Auto-stats did not issue ANALYZE on tableoid %d since the user does not have table-owner level permissions.", relationOid); return; } relation = makeRangeVar(get_namespace_name(get_rel_namespace(relationOid)), get_rel_name(relationOid), -1); analyzeStmt = makeNode(VacuumStmt); /* Set up command parameters */ analyzeStmt->vacuum = false; analyzeStmt->full = false; analyzeStmt->analyze = true; analyzeStmt->freeze_min_age = -1; analyzeStmt->verbose = false; analyzeStmt->rootonly = false; analyzeStmt->relation = relation; /* not used since we pass relids list */ analyzeStmt->va_cols = NIL; vacuum(analyzeStmt, NIL, NULL, false, false); pfree(analyzeStmt); }
/* * SetLocktagRelationOid * Set up a locktag for a relation, given only relation OID */ static inline void SetLocktagRelationOid(LOCKTAG *tag, Oid relid) { Oid dbid; if (IsSharedRelation(relid)) dbid = InvalidOid; else dbid = MyDatabaseId; SET_LOCKTAG_RELATION(*tag, dbid, relid); }
/* * CacheInvalidateCatalog * Register invalidation of the whole content of a system catalog. * * This is normally used in VACUUM FULL/CLUSTER, where we haven't so much * changed any tuples as moved them around. Some uses of catcache entries * expect their TIDs to be correct, so we have to blow away the entries. * * Note: we expect caller to verify that the rel actually is a system * catalog. If it isn't, no great harm is done, just a wasted sinval message. */ void CacheInvalidateCatalog(Oid catalogId) { Oid databaseId; if (IsSharedRelation(catalogId)) databaseId = InvalidOid; else databaseId = MyDatabaseId; RegisterCatalogInvalidation(databaseId, catalogId); }
/* * CacheInvalidateHeapTuple * Register the given tuple for invalidation at end of command * (ie, current command is creating or outdating this tuple). * Also, detect whether a relcache invalidation is implied. * * For an insert or delete, tuple is the target tuple and newtuple is NULL. * For an update, we are called just once, with tuple being the old tuple * version and newtuple the new version. This allows avoidance of duplicate * effort during an update. */ void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple) { Oid tupleRelId; Oid databaseId; Oid relationId; /* Do nothing during bootstrap */ if (IsBootstrapProcessingMode()) return; /* * We only need to worry about invalidation for tuples that are in system * catalogs; user-relation tuples are never in catcaches and can't affect * the relcache either. */ if (!IsCatalogRelation(relation)) return; /* * IsCatalogRelation() will return true for TOAST tables of system * catalogs, but we don't care about those, either. */ if (IsToastRelation(relation)) return; /* * If we're not prepared to queue invalidation messages for this * subtransaction level, get ready now. */ PrepareInvalidationState(); /* * First let the catcache do its thing */ tupleRelId = RelationGetRelid(relation); if (RelationInvalidatesSnapshotsOnly(tupleRelId)) { databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId; RegisterSnapshotInvalidation(databaseId, tupleRelId); } else PrepareToInvalidateCacheTuple(relation, tuple, newtuple, RegisterCatcacheInvalidation); /* * Now, is this tuple one of the primary definers of a relcache entry? * * Note we ignore newtuple here; we assume an update cannot move a tuple * from being part of one relcache entry to being part of another. */ if (tupleRelId == RelationRelationId) { Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple); relationId = HeapTupleGetOid(tuple); if (classtup->relisshared) databaseId = InvalidOid; else databaseId = MyDatabaseId; } else if (tupleRelId == AttributeRelationId) { Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple); relationId = atttup->attrelid; /* * KLUGE ALERT: we always send the relcache event with MyDatabaseId, * even if the rel in question is shared (which we can't easily tell). * This essentially means that only backends in this same database * will react to the relcache flush request. This is in fact * appropriate, since only those backends could see our pg_attribute * change anyway. It looks a bit ugly though. (In practice, shared * relations can't have schema changes after bootstrap, so we should * never come here for a shared rel anyway.) */ databaseId = MyDatabaseId; } else if (tupleRelId == IndexRelationId) { Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple); /* * When a pg_index row is updated, we should send out a relcache inval * for the index relation. As above, we don't know the shared status * of the index, but in practice it doesn't matter since indexes of * shared catalogs can't have such updates. */ relationId = indextup->indexrelid; databaseId = MyDatabaseId; } else return; /* * Yes. We need to register a relcache invalidation event. */ RegisterRelcacheInvalidation(databaseId, relationId); }
/* * Translate an object name and arguments (as passed by the parser) to an * ObjectAddress. * * The returned object will be locked using the specified lockmode. If a * sub-object is looked up, the parent object will be locked instead. * * If the object is a relation or a child object of a relation (e.g. an * attribute or contraint), the relation is also opened and *relp receives * the open relcache entry pointer; otherwise, *relp is set to NULL. This * is a bit grotty but it makes life simpler, since the caller will * typically need the relcache entry too. Caller must close the relcache * entry when done with it. The relation is locked with the specified lockmode * if the target object is the relation itself or an attribute, but for other * child objects, only AccessShareLock is acquired on the relation. * * We don't currently provide a function to release the locks acquired here; * typically, the lock must be held until commit to guard against a concurrent * drop operation. */ ObjectAddress get_object_address(ObjectType objtype, List *objname, List *objargs, Relation *relp, LOCKMODE lockmode) { ObjectAddress address; Relation relation = NULL; /* Some kind of lock must be taken. */ Assert(lockmode != NoLock); switch (objtype) { case OBJECT_INDEX: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: case OBJECT_FOREIGN_TABLE: relation = get_relation_by_qualified_name(objtype, objname, lockmode); address.classId = RelationRelationId; address.objectId = RelationGetRelid(relation); address.objectSubId = 0; break; case OBJECT_COLUMN: address = get_object_address_attribute(objtype, objname, &relation, lockmode); break; case OBJECT_RULE: case OBJECT_TRIGGER: case OBJECT_CONSTRAINT: address = get_object_address_relobject(objtype, objname, &relation); break; case OBJECT_DATABASE: case OBJECT_EXTENSION: case OBJECT_TABLESPACE: case OBJECT_ROLE: case OBJECT_SCHEMA: case OBJECT_LANGUAGE: case OBJECT_FDW: case OBJECT_FOREIGN_SERVER: address = get_object_address_unqualified(objtype, objname); break; case OBJECT_TYPE: case OBJECT_DOMAIN: address.classId = TypeRelationId; address.objectId = typenameTypeId(NULL, makeTypeNameFromNameList(objname)); address.objectSubId = 0; break; case OBJECT_AGGREGATE: address.classId = ProcedureRelationId; address.objectId = LookupAggNameTypeNames(objname, objargs, false); address.objectSubId = 0; break; case OBJECT_FUNCTION: address.classId = ProcedureRelationId; address.objectId = LookupFuncNameTypeNames(objname, objargs, false); address.objectSubId = 0; break; case OBJECT_OPERATOR: Assert(list_length(objargs) == 2); address.classId = OperatorRelationId; address.objectId = LookupOperNameTypeNames(NULL, objname, (TypeName *) linitial(objargs), (TypeName *) lsecond(objargs), false, -1); address.objectSubId = 0; break; case OBJECT_COLLATION: address.classId = CollationRelationId; address.objectId = get_collation_oid(objname, false); address.objectSubId = 0; break; case OBJECT_CONVERSION: address.classId = ConversionRelationId; address.objectId = get_conversion_oid(objname, false); address.objectSubId = 0; break; case OBJECT_OPCLASS: case OBJECT_OPFAMILY: address = get_object_address_opcf(objtype, objname, objargs); break; case OBJECT_LARGEOBJECT: Assert(list_length(objname) == 1); address.classId = LargeObjectRelationId; address.objectId = oidparse(linitial(objname)); address.objectSubId = 0; if (!LargeObjectExists(address.objectId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", address.objectId))); break; case OBJECT_CAST: { TypeName *sourcetype = (TypeName *) linitial(objname); TypeName *targettype = (TypeName *) linitial(objargs); Oid sourcetypeid = typenameTypeId(NULL, sourcetype); Oid targettypeid = typenameTypeId(NULL, targettype); address.classId = CastRelationId; address.objectId = get_cast_oid(sourcetypeid, targettypeid, false); address.objectSubId = 0; } break; case OBJECT_TSPARSER: address.classId = TSParserRelationId; address.objectId = get_ts_parser_oid(objname, false); address.objectSubId = 0; break; case OBJECT_TSDICTIONARY: address.classId = TSDictionaryRelationId; address.objectId = get_ts_dict_oid(objname, false); address.objectSubId = 0; break; case OBJECT_TSTEMPLATE: address.classId = TSTemplateRelationId; address.objectId = get_ts_template_oid(objname, false); address.objectSubId = 0; break; case OBJECT_TSCONFIGURATION: address.classId = TSConfigRelationId; address.objectId = get_ts_config_oid(objname, false); address.objectSubId = 0; break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); /* placate compiler, in case it thinks elog might return */ address.classId = InvalidOid; address.objectId = InvalidOid; address.objectSubId = 0; } /* * If we're dealing with a relation or attribute, then the relation is * already locked. If we're dealing with any other type of object, we need * to lock it and then verify that it still exists. */ if (address.classId != RelationRelationId) { if (IsSharedRelation(address.classId)) LockSharedObject(address.classId, address.objectId, 0, lockmode); else LockDatabaseObject(address.classId, address.objectId, 0, lockmode); /* Did it go away while we were waiting for the lock? */ if (!object_exists(address)) elog(ERROR, "cache lookup failed for class %u object %u subobj %d", address.classId, address.objectId, address.objectSubId); } /* Return the object address and the relation. */ *relp = relation; return address; }