/* ---------------------------------------------------------------- * UpdateRelationRelation * ---------------------------------------------------------------- */ static void UpdateRelationRelation(Relation indexRelation) { Relation pg_class; HeapTuple tuple; pg_class = heap_openr(RelationRelationName, RowExclusiveLock); /* XXX Natts_pg_class_fixed is a hack - see pg_class.h */ tuple = heap_addheader(Natts_pg_class_fixed, true, CLASS_TUPLE_SIZE, (void *) indexRelation->rd_rel); /* * the new tuple must have the oid already chosen for the index. sure * would be embarrassing to do this sort of thing in polite company. */ HeapTupleSetOid(tuple, RelationGetRelid(indexRelation)); simple_heap_insert(pg_class, tuple); /* update the system catalog indexes */ CatalogUpdateIndexes(pg_class, tuple); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); }
/* * Open pg_largeobject and its index, if not already done in current xact */ static void open_lo_relation(void) { ResourceOwner currentOwner; if (lo_heap_r && lo_index_r) return; /* already open in current xact */ /* Arrange for the top xact to own these relation references */ currentOwner = CurrentResourceOwner; PG_TRY(); { CurrentResourceOwner = TopTransactionResourceOwner; /* Use RowExclusiveLock since we might either read or write */ if (lo_heap_r == NULL) lo_heap_r = heap_openr(LargeObjectRelationName, RowExclusiveLock); if (lo_index_r == NULL) lo_index_r = index_openr(LargeObjectLOidPNIndex); } PG_CATCH(); { /* Ensure CurrentResourceOwner is restored on error */ CurrentResourceOwner = currentOwner; PG_RE_THROW(); } PG_END_TRY(); CurrentResourceOwner = currentOwner; }
/* *-------------------------------------------------------------- * Async_UnlistenAll * * Unlisten all relations for this backend. * * This is invoked by UNLISTEN "*" command, and also at backend exit. * * Results: * XXX * * Side effects: * pg_listener is updated. * *-------------------------------------------------------------- */ static void Async_UnlistenAll(void) { Relation lRel; TupleDesc tdesc; HeapScanDesc scan; HeapTuple lTuple; ScanKeyData key[1]; if (Trace_notify) elog(DEBUG1, "Async_UnlistenAll"); lRel = heap_openr(ListenerRelationName, ExclusiveLock); tdesc = RelationGetDescr(lRel); /* Find and delete all entries with my listenerPID */ ScanKeyEntryInitialize(&key[0], 0, Anum_pg_listener_pid, F_INT4EQ, Int32GetDatum(MyProcPid)); scan = heap_beginscan(lRel, SnapshotNow, 1, key); while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) simple_heap_delete(lRel, &lTuple->t_self); heap_endscan(scan); heap_close(lRel, ExclusiveLock); }
/* * Rename conversion */ void RenameConversion(List *name, const char *newname) { Oid conversionOid; Oid namespaceOid; HeapTuple tup; Relation rel; AclResult aclresult; rel = heap_openr(ConversionRelationName, RowExclusiveLock); conversionOid = FindConversionByName(name); if (!OidIsValid(conversionOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("conversion \"%s\" does not exist", NameListToString(name)))); tup = SearchSysCacheCopy(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); namespaceOid = ((Form_pg_conversion) GETSTRUCT(tup))->connamespace; /* make sure the new name doesn't exist */ if (SearchSysCacheExists(CONNAMENSP, CStringGetDatum(newname), ObjectIdGetDatum(namespaceOid), 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conversion \"%s\" already exists in schema \"%s\"", newname, get_namespace_name(namespaceOid)))); /* must be owner */ if (!superuser() && ((Form_pg_conversion) GETSTRUCT(tup))->conowner != GetUserId()) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameListToString(name)); /* must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); /* rename */ namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); }
/* * setNewRelfilenode - assign a new relfilenode value to the relation * * Caller must already hold exclusive lock on the relation. */ void setNewRelfilenode(Relation relation) { Oid newrelfilenode; Relation pg_class; HeapTuple tuple; Form_pg_class rd_rel; RelationData workrel; /* Can't change relfilenode for nailed tables (indexes ok though) */ Assert(!relation->rd_isnailed || relation->rd_rel->relkind == RELKIND_INDEX); /* Can't change for shared tables or indexes */ Assert(!relation->rd_rel->relisshared); /* Allocate a new relfilenode */ newrelfilenode = newoid(); /* * Find the pg_class tuple for the given relation. This is not used * during bootstrap, so okay to use heap_update always. */ pg_class = heap_openr(RelationRelationName, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(RelationGetRelid(relation)), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", RelationGetRelid(relation)); rd_rel = (Form_pg_class) GETSTRUCT(tuple); /* create another storage file. Is it a little ugly ? */ /* NOTE: any conflict in relfilenode value will be caught here */ memcpy((char *) &workrel, relation, sizeof(RelationData)); workrel.rd_fd = -1; workrel.rd_node.relNode = newrelfilenode; heap_storage_create(&workrel); smgrclose(DEFAULT_SMGR, &workrel); /* schedule unlinking old relfilenode */ smgrunlink(DEFAULT_SMGR, relation); /* update the pg_class row */ rd_rel->relfilenode = newrelfilenode; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); /* Make sure the relfilenode change is visible */ CommandCounterIncrement(); }
/* * Change conversion owner */ void AlterConversionOwner(List *name, AclId newOwnerSysId) { Oid conversionOid; HeapTuple tup; Relation rel; Form_pg_conversion convForm; rel = heap_openr(ConversionRelationName, RowExclusiveLock); conversionOid = FindConversionByName(name); if (!OidIsValid(conversionOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("conversion \"%s\" does not exist", NameListToString(name)))); tup = SearchSysCacheCopy(CONOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for conversion %u", conversionOid); convForm = (Form_pg_conversion) GETSTRUCT(tup); /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ if (convForm->conowner != newOwnerSysId) { /* Otherwise, must be superuser to change object ownership */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to change owner"))); /* * Modify the owner --- okay to scribble on tup because it's a * copy */ convForm->conowner = newOwnerSysId; simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); } heap_close(rel, NoLock); heap_freetuple(tup); }
/* ---------------------------------------------------------------- * AppendAttributeTuples * ---------------------------------------------------------------- */ static void AppendAttributeTuples(Relation indexRelation, int numatts) { Relation pg_attribute; CatalogIndexState indstate; TupleDesc indexTupDesc; HeapTuple new_tuple; int i; /* * open the attribute relation and its indexes */ pg_attribute = heap_openr(AttributeRelationName, RowExclusiveLock); indstate = CatalogOpenIndexes(pg_attribute); /* * insert data from new index's tupdesc into pg_attribute */ indexTupDesc = RelationGetDescr(indexRelation); for (i = 0; i < numatts; i++) { /* * There used to be very grotty code here to set these fields, but * I think it's unnecessary. They should be set already. */ Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); new_tuple = heap_addheader(Natts_pg_attribute, false, ATTRIBUTE_TUPLE_SIZE, (void *) indexTupDesc->attrs[i]); simple_heap_insert(pg_attribute, new_tuple); CatalogIndexInsert(indstate, new_tuple); heap_freetuple(new_tuple); } CatalogCloseIndexes(indstate); heap_close(pg_attribute, RowExclusiveLock); }
/* * InsertRule - * takes the arguments and inserts them as attributes into the system * relation "pg_rewrite" * * MODS : changes the value of LastOidProcessed as a side * effect of inserting the rule tuple * * ARGS : rulname - name of the rule * evtype - one of RETRIEVE,REPLACE,DELETE,APPEND * evobj - name of relation * evslot - comma delimited list of slots * if null => multi-attr rule * evinstead - is an instead rule * actiontree - parsetree(s) of rule action */ static Oid InsertRule(char *rulname, int evtype, char *evobj, char *evslot, char *evqual, bool evinstead, char *actiontree) { static char rulebuf[RULE_PLAN_SIZE]; static char actionbuf[RULE_PLAN_SIZE]; static char qualbuf[RULE_PLAN_SIZE]; Oid eventrel_oid = InvalidOid; AttrNumber evslot_index = InvalidAttrNumber; Relation eventrel = NULL; char *is_instead = "f"; extern void eval_as_new_xact(); char *template; eventrel = heap_openr(evobj); if (eventrel == NULL) { elog(WARN, "rules cannot be defined on relations not in schema"); } eventrel_oid = RelationGetRelationId(eventrel); /* * if the slotname is null, we know that this is a multi-attr * rule */ if (evslot == NULL) evslot_index = -1; else evslot_index = varattno(eventrel, (char*)evslot); heap_close(eventrel); if (evinstead) is_instead = "t"; if (evqual == NULL) evqual = "nil"; if (IsDefinedRewriteRule(rulname)) elog(WARN, "Attempt to insert rule '%s' failed: already exists", rulname); strcpyq(actionbuf,actiontree); strcpyq(qualbuf, evqual);
/* *-------------------------------------------------------------- * Async_Unlisten * * This is executed by the SQL unlisten command. * * Remove the backend from the list of listening backends * for the specified relation. * * Results: * XXX * * Side effects: * pg_listener is updated. * *-------------------------------------------------------------- */ void Async_Unlisten(char *relname, int pid) { Relation lRel; HeapScanDesc scan; HeapTuple tuple; /* Handle specially the `unlisten "*"' command */ if ((!relname) || (*relname == '\0') || (strcmp(relname, "*") == 0)) { Async_UnlistenAll(); return; } if (Trace_notify) elog(DEBUG1, "Async_Unlisten(%s,%d)", relname, pid); lRel = heap_openr(ListenerRelationName, ExclusiveLock); scan = heap_beginscan(lRel, SnapshotNow, 0, (ScanKey) NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(tuple); if (listener->listenerpid == pid && strncmp(NameStr(listener->relname), relname, NAMEDATALEN) == 0) { /* Found the matching tuple, delete it */ simple_heap_delete(lRel, &tuple->t_self); /* * We assume there can be only one match, so no need to scan * the rest of the table */ break; } } heap_endscan(scan); heap_close(lRel, ExclusiveLock); /* * We do not complain about unlistening something not being listened; * should we? */ }
/* * Returns true if at least one user is defined in this database cluster. */ static bool ThereIsAtLeastOneUser(void) { Relation pg_shadow_rel; TupleDesc pg_shadow_dsc; HeapScanDesc scan; bool result; pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock); pg_shadow_dsc = RelationGetDescr(pg_shadow_rel); scan = heap_beginscan(pg_shadow_rel, SnapshotNow, 0, NULL); result = (heap_getnext(scan, ForwardScanDirection) != NULL); heap_endscan(scan); heap_close(pg_shadow_rel, AccessExclusiveLock); return result; }
/* * SetRelationRuleStatus * Set the value of the relation's relhasrules field in pg_class; * if the relation is becoming a view, also adjust its relkind. * * NOTE: caller must be holding an appropriate lock on the relation. * * NOTE: an important side-effect of this operation is that an SI invalidation * message is sent out to all backends --- including me --- causing relcache * entries to be flushed or updated with the new set of rules for the table. * This must happen even if we find that no change is needed in the pg_class * row. */ void SetRelationRuleStatus(Oid relationId, bool relHasRules, bool relIsBecomingView) { Relation relationRelation; HeapTuple tuple; Form_pg_class classForm; /* * Find the tuple to update in pg_class, using syscache for the * lookup. */ relationRelation = heap_openr(RelationRelationName, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relationId), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relationId); classForm = (Form_pg_class) GETSTRUCT(tuple); if (classForm->relhasrules != relHasRules || (relIsBecomingView && classForm->relkind != RELKIND_VIEW)) { /* Do the update */ classForm->relhasrules = relHasRules; if (relIsBecomingView) classForm->relkind = RELKIND_VIEW; simple_heap_update(relationRelation, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ CatalogUpdateIndexes(relationRelation, tuple); } else { /* no need to change tuple, but force relcache rebuild anyway */ CacheInvalidateRelcacheByTuple(tuple); } heap_freetuple(tuple); heap_close(relationRelation, RowExclusiveLock); }
/* * SetDefine - converts query string defining set to an oid * * We create an SQL function having the given querystring as its body. * The name of the function is then changed to use the OID of its tuple * in pg_proc. */ Oid SetDefine(char *querystr, Oid elemType) { Oid setoid; char *procname = GENERICSETNAME; char *fileName = "-"; char realprocname[NAMEDATALEN]; HeapTuple tup, newtup = NULL; Form_pg_proc proc; Relation procrel; int i; Datum replValue[Natts_pg_proc]; char replNull[Natts_pg_proc]; char repl[Natts_pg_proc]; setoid = ProcedureCreate(procname, /* changed below, after oid known */ PG_CATALOG_NAMESPACE, /* XXX wrong */ false, /* don't replace */ true, /* returnsSet */ elemType, /* returnType */ SQLlanguageId, /* language */ F_FMGR_SQL_VALIDATOR, querystr, /* prosrc */ fileName, /* probin */ false, /* not aggregate */ false, /* security invoker */ false, /* isStrict (irrelevant, no args) */ PROVOLATILE_VOLATILE, /* assume unsafe */ 0, /* parameterCount */ NULL); /* parameterTypes */ /* * Since we're still inside this command of the transaction, we can't * see the results of the procedure definition unless we pretend we've * started the next command. (Postgres's solution to the Halloween * problem is to not allow you to see the results of your command * until you start the next command.) */ CommandCounterIncrement(); procrel = heap_openr(ProcedureRelationName, RowExclusiveLock); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(setoid), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", setoid); /* * We can tell whether the set was already defined by checking the * name. If it's GENERICSETNAME, the set is new. If it's "set<some * oid>" it's already defined. */ proc = (Form_pg_proc) GETSTRUCT(tup); if (strcmp(procname, NameStr(proc->proname)) == 0) { /* make the real proc name */ snprintf(realprocname, sizeof(realprocname), "set%u", setoid); /* set up the attributes to be modified or kept the same */ repl[0] = 'r'; for (i = 1; i < Natts_pg_proc; i++) repl[i] = ' '; replValue[0] = (Datum) realprocname; for (i = 1; i < Natts_pg_proc; i++) replValue[i] = (Datum) 0; for (i = 0; i < Natts_pg_proc; i++) replNull[i] = ' '; /* change the pg_proc tuple */ newtup = heap_modifytuple(tup, procrel, replValue, replNull, repl); simple_heap_update(procrel, &newtup->t_self, newtup); setoid = HeapTupleGetOid(newtup); CatalogUpdateIndexes(procrel, newtup); heap_freetuple(newtup); } ReleaseSysCache(tup); heap_close(procrel, RowExclusiveLock); return setoid; }
/* ---------------- * UpdateStats * * Update pg_class' relpages and reltuples statistics for the given relation * (which can be either a table or an index). Note that this is not used * in the context of VACUUM. * ---------------- */ void UpdateStats(Oid relid, double reltuples) { Relation whichRel; Relation pg_class; HeapTuple tuple; BlockNumber relpages; Form_pg_class rd_rel; HeapScanDesc pg_class_scan = NULL; bool in_place_upd; /* * This routine handles updates for both the heap and index relation * statistics. In order to guarantee that we're able to *see* the * index relation tuple, we bump the command counter id here. The * index relation tuple was created in the current transaction. */ CommandCounterIncrement(); /* * CommandCounterIncrement() flushes invalid cache entries, including * those for the heap and index relations for which we're updating * statistics. Now that the cache is flushed, it's safe to open the * relation again. We need the relation open in order to figure out * how many blocks it contains. */ /* * Grabbing lock here is probably redundant ... */ whichRel = relation_open(relid, ShareLock); /* * Find the tuple to update in pg_class. Normally we make a copy of * the tuple using the syscache, modify it, and apply heap_update. * But in bootstrap mode we can't use heap_update, so we cheat and * overwrite the tuple in-place. * * We also must cheat if reindexing pg_class itself, because the * target index may presently not be part of the set of indexes that * CatalogUpdateIndexes would update (see reindex_relation). In this * case the stats updates will not be WAL-logged and so could be lost * in a crash. This seems OK considering VACUUM does the same thing. */ pg_class = heap_openr(RelationRelationName, RowExclusiveLock); in_place_upd = IsBootstrapProcessingMode() || ReindexIsProcessingHeap(RelationGetRelid(pg_class)); if (!in_place_upd) { tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); } else { ScanKeyData key[1]; ScanKeyEntryInitialize(&key[0], 0, ObjectIdAttributeNumber, F_OIDEQ, ObjectIdGetDatum(relid)); pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key); tuple = heap_getnext(pg_class_scan, ForwardScanDirection); } if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", relid); rd_rel = (Form_pg_class) GETSTRUCT(tuple); /* * Figure values to insert. * * If we found zero tuples in the scan, do NOT believe it; instead put a * bogus estimate into the statistics fields. Otherwise, the common * pattern "CREATE TABLE; CREATE INDEX; insert data" leaves the table * with zero size statistics until a VACUUM is done. The optimizer * will generate very bad plans if the stats claim the table is empty * when it is actually sizable. See also CREATE TABLE in heap.c. * * Note: this path is also taken during bootstrap, because bootstrap.c * passes reltuples = 0 after loading a table. We have to estimate * some number for reltuples based on the actual number of pages. */ relpages = RelationGetNumberOfBlocks(whichRel); if (reltuples == 0) { if (relpages == 0) { /* Bogus defaults for a virgin table, same as heap.c */ reltuples = 1000; relpages = 10; } else if (whichRel->rd_rel->relkind == RELKIND_INDEX && relpages <= 2) { /* Empty index, leave bogus defaults in place */ reltuples = 1000; } else reltuples = ((double) relpages) * NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts); } /* * Update statistics in pg_class, if they changed. (Avoiding an * unnecessary update is not just a tiny performance improvement; it * also reduces the window wherein concurrent CREATE INDEX commands * may conflict.) */ if (rd_rel->relpages != (int32) relpages || rd_rel->reltuples != (float4) reltuples) { if (in_place_upd) { /* Bootstrap or reindex case: overwrite fields in place. */ LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); rd_rel->relpages = (int32) relpages; rd_rel->reltuples = (float4) reltuples; LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK); WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); if (!IsBootstrapProcessingMode()) CacheInvalidateHeapTuple(pg_class, tuple); } else { /* During normal processing, must work harder. */ rd_rel->relpages = (int32) relpages; rd_rel->reltuples = (float4) reltuples; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); } } if (!pg_class_scan) heap_freetuple(tuple); else heap_endscan(pg_class_scan); /* * We shouldn't have to do this, but we do... Modify the reldesc in * place with the new values so that the cache contains the latest * copy. (XXX is this really still necessary? The relcache will get * fixed at next CommandCounterIncrement, so why bother here?) */ whichRel->rd_rel->relpages = (int32) relpages; whichRel->rd_rel->reltuples = (float4) reltuples; heap_close(pg_class, RowExclusiveLock); relation_close(whichRel, NoLock); }
/* * regtypein - converts "typename" to type OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. * * In bootstrap mode the name must just equal some existing name in pg_type. * In normal mode the type name can be specified using the full type syntax * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will * work and be translated to the correct type names. (We ignore any typmod * info generated by the parser, however.) */ Datum regtypein(PG_FUNCTION_ARGS) { char *typ_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; int32 typmod; /* '-' ? */ if (strcmp(typ_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (typ_name_or_oid[0] >= '0' && typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a type name, possibly schema-qualified or decorated */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_type for a match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_type_typname, (RegProcedure) F_NAMEEQ, CStringGetDatum(typ_name_or_oid)); hdesc = heap_openr(TypeRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, TypeNameNspIndex, true, SnapshotNow, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typ_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: invoke the full parser to deal with special cases such * as array syntax. */ parseTypeString(typ_name_or_oid, &result, &typmod); PG_RETURN_OID(result); }
/* * regclassin - converts "classname" to class OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_class entry. */ Datum regclassin(PG_FUNCTION_ARGS) { char *class_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; List *names; /* '-' ? */ if (strcmp(class_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (class_name_or_oid[0] >= '0' && class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(class_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_class for a match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_class_relname, (RegProcedure) F_NAMEEQ, CStringGetDatum(class_name_or_oid)); hdesc = heap_openr(RelationRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, ClassNameNspIndex, true, SnapshotNow, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = HeapTupleGetOid(tuple); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", class_name_or_oid))); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it matches * any pg_class entries in the current search path. */ names = stringToQualifiedNameList(class_name_or_oid, "regclassin"); result = RangeVarGetRelid(makeRangeVarFromNameList(names), false); PG_RETURN_OID(result); }
/* * regprocin - converts "proname" to proc OID * * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. */ Datum regprocin(PG_FUNCTION_ARGS) { char *pro_name_or_oid = PG_GETARG_CSTRING(0); RegProcedure result = InvalidOid; List *names; FuncCandidateList clist; /* '-' ? */ if (strcmp(pro_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (pro_name_or_oid[0] >= '0' && pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_proc for a unique match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { int matches = 0; Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_proc_proname, (RegProcedure) F_NAMEEQ, CStringGetDatum(pro_name_or_oid)); hdesc = heap_openr(ProcedureRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true, SnapshotNow, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { result = (RegProcedure) HeapTupleGetOid(tuple); if (++matches > 1) break; } systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); if (matches == 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function \"%s\" does not exist", pro_name_or_oid))); else if (matches > 1) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("more than one function named \"%s\"", pro_name_or_oid))); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it matches * any pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid, "regprocin"); clist = FuncnameGetCandidates(names, -1); if (clist == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function \"%s\" does not exist", pro_name_or_oid))); else if (clist->next != NULL) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("more than one function named \"%s\"", pro_name_or_oid))); result = clist->oid; PG_RETURN_OID(result); }
/* * index-info-- * Retrieves catalog information on an index on a given relation. * * The index relation is opened on the first invocation. The current * retrieves the next index relation within the catalog that has not * already been retrieved by a previous call. The index catalog * is closed when no more indices for 'relid' can be found. * * 'first' is 1 if this is the first call * * Returns true if successful and false otherwise. Index info is returned * via the transient data structure 'info'. * */ bool index_info(Query *root, bool first, int relid, IdxInfoRetval *info) { register i; HeapTuple indexTuple, amopTuple; IndexTupleForm index; Relation indexRelation; uint16 amstrategy; Oid relam; Oid indrelid; static Relation relation = (Relation) NULL; static HeapScanDesc scan = (HeapScanDesc) NULL; static ScanKeyData indexKey; /* find the oid of the indexed relation */ indrelid = getrelid(relid, root->rtable); memset(info, 0, sizeof(IdxInfoRetval)); /* * the maximum number of elements in each of the following arrays is * 8. We allocate one more for a terminating 0 to indicate the end * of the array. */ info->indexkeys = (int *)palloc(sizeof(int)*9); memset(info->indexkeys, 0, sizeof(int)*9); info->orderOprs = (Oid *)palloc(sizeof(Oid)*9); memset(info->orderOprs, 0, sizeof(Oid)*9); info->classlist = (Oid *)palloc(sizeof(Oid)*9); memset(info->classlist, 0, sizeof(Oid)*9); /* Find an index on the given relation */ if (first) { if (RelationIsValid(relation)) heap_close(relation); if (HeapScanIsValid(scan)) heap_endscan(scan); ScanKeyEntryInitialize(&indexKey, 0, Anum_pg_index_indrelid, F_OIDEQ, ObjectIdGetDatum(indrelid)); relation = heap_openr(IndexRelationName); scan = heap_beginscan(relation, 0, NowTimeQual, 1, &indexKey); } if (!HeapScanIsValid(scan)) elog(WARN, "index_info: scan not started"); indexTuple = heap_getnext(scan, 0, (Buffer *) NULL); if (!HeapTupleIsValid(indexTuple)) { heap_endscan(scan); heap_close(relation); scan = (HeapScanDesc) NULL; relation = (Relation) NULL; return(0); } /* Extract info from the index tuple */ index = (IndexTupleForm)GETSTRUCT(indexTuple); info->relid = index->indexrelid; /* index relation */ for (i = 0; i < 8; i++) info->indexkeys[i] = index->indkey[i]; for (i = 0; i < 8; i++) info->classlist[i] = index->indclass[i]; info->indproc = index->indproc; /* functional index ?? */ /* partial index ?? */ if (VARSIZE(&index->indpred) != 0) { /* * The memory allocated here for the predicate (in lispReadString) * only needs to stay around until it's used in find_index_paths, * which is all within a command, so the automatic pfree at end * of transaction should be ok. */ char *predString; predString = fmgr(F_TEXTOUT, &index->indpred); info->indpred = (Node*)stringToNode(predString); pfree(predString); } /* Extract info from the relation descriptor for the index */ indexRelation = index_open(index->indexrelid); #ifdef notdef /* XXX should iterate through strategies -- but how? use #1 for now */ amstrategy = indexRelation->rd_am->amstrategies; #endif /* notdef */ amstrategy = 1; relam = indexRelation->rd_rel->relam; info->relam = relam; info->pages = indexRelation->rd_rel->relpages; info->tuples = indexRelation->rd_rel->reltuples; heap_close(indexRelation); /* * Find the index ordering keys * * Must use indclass to know when to stop looking since with * functional indices there could be several keys (args) for * one opclass. -mer 27 Sept 1991 */ for (i = 0; i < 8 && index->indclass[i]; ++i) { amopTuple = SearchSysCacheTuple(AMOPSTRATEGY, ObjectIdGetDatum(relam), ObjectIdGetDatum(index->indclass[i]), UInt16GetDatum(amstrategy), 0); if (!HeapTupleIsValid(amopTuple)) elog(WARN, "index_info: no amop %d %d %d", relam, index->indclass[i], amstrategy); info->orderOprs[i] = ((Form_pg_amop)GETSTRUCT(amopTuple))->amopopr; } return(TRUE); }
/* ---------------- * set relhasindex of relation's pg_class entry * * If isprimary is TRUE, we are defining a primary index, so also set * relhaspkey to TRUE. Otherwise, leave relhaspkey alone. * * If reltoastidxid is not InvalidOid, also set reltoastidxid to that value. * This is only used for TOAST relations. * * NOTE: an important side-effect of this operation is that an SI invalidation * message is sent out to all backends --- including me --- causing relcache * entries to be flushed or updated with the new hasindex data. This must * happen even if we find that no change is needed in the pg_class row. * ---------------- */ void setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid) { Relation pg_class; HeapTuple tuple; Form_pg_class classtuple; bool dirty = false; HeapScanDesc pg_class_scan = NULL; /* * Find the tuple to update in pg_class. In bootstrap mode we can't * use heap_update, so cheat and overwrite the tuple in-place. In * normal processing, make a copy to scribble on. */ pg_class = heap_openr(RelationRelationName, RowExclusiveLock); if (!IsBootstrapProcessingMode()) { tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); } else { ScanKeyData key[1]; ScanKeyEntryInitialize(&key[0], 0, ObjectIdAttributeNumber, F_OIDEQ, ObjectIdGetDatum(relid)); pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key); tuple = heap_getnext(pg_class_scan, ForwardScanDirection); } if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", relid); classtuple = (Form_pg_class) GETSTRUCT(tuple); /* Apply required updates */ if (pg_class_scan) LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); if (classtuple->relhasindex != hasindex) { classtuple->relhasindex = hasindex; dirty = true; } if (isprimary) { if (!classtuple->relhaspkey) { classtuple->relhaspkey = true; dirty = true; } } if (OidIsValid(reltoastidxid)) { Assert(classtuple->relkind == RELKIND_TOASTVALUE); if (classtuple->reltoastidxid != reltoastidxid) { classtuple->reltoastidxid = reltoastidxid; dirty = true; } } if (pg_class_scan) LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK); if (pg_class_scan) { /* Write the modified tuple in-place */ WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); /* Send out shared cache inval if necessary */ if (!IsBootstrapProcessingMode()) CacheInvalidateHeapTuple(pg_class, tuple); BufferSync(); } else if (dirty) { simple_heap_update(pg_class, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ CatalogUpdateIndexes(pg_class, tuple); } else { /* no need to change tuple, but force relcache rebuild anyway */ CacheInvalidateRelcache(relid); } if (!pg_class_scan) heap_freetuple(tuple); else heap_endscan(pg_class_scan); heap_close(pg_class, RowExclusiveLock); }
/* *-------------------------------------------------------------- * AtCommit_Notify * * This is called at transaction commit. * * If there are outbound notify requests in the pendingNotifies list, * scan pg_listener for matching tuples, and either signal the other * backend or send a message to our own frontend. * * NOTE: we are still inside the current transaction, therefore can * piggyback on its committing of changes. * * Results: * XXX * * Side effects: * Tuples in pg_listener that have matching relnames and other peoples' * listenerPIDs are updated with a nonzero notification field. * *-------------------------------------------------------------- */ void AtCommit_Notify(void) { Relation lRel; TupleDesc tdesc; HeapScanDesc scan; HeapTuple lTuple, rTuple; Datum value[Natts_pg_listener]; char repl[Natts_pg_listener], nulls[Natts_pg_listener]; if (pendingNotifies == NIL) return; /* no NOTIFY statements in this * transaction */ /* * NOTIFY is disabled if not normal processing mode. This test used to * be in xact.c, but it seems cleaner to do it here. */ if (!IsNormalProcessingMode()) { ClearPendingNotifies(); return; } if (Trace_notify) elog(DEBUG1, "AtCommit_Notify"); /* preset data to update notify column to MyProcPid */ nulls[0] = nulls[1] = nulls[2] = ' '; repl[0] = repl[1] = repl[2] = ' '; repl[Anum_pg_listener_notify - 1] = 'r'; value[0] = value[1] = value[2] = (Datum) 0; value[Anum_pg_listener_notify - 1] = Int32GetDatum(MyProcPid); lRel = heap_openr(ListenerRelationName, ExclusiveLock); tdesc = RelationGetDescr(lRel); scan = heap_beginscan(lRel, SnapshotNow, 0, (ScanKey) NULL); while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(lTuple); char *relname = NameStr(listener->relname); int32 listenerPID = listener->listenerpid; if (!AsyncExistsPendingNotify(relname)) continue; if (listenerPID == MyProcPid) { /* * Self-notify: no need to bother with table update. Indeed, * we *must not* clear the notification field in this path, or * we could lose an outside notify, which'd be bad for * applications that ignore self-notify messages. */ if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: notifying self"); NotifyMyFrontEnd(relname, listenerPID); } else { if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: notifying pid %d", listenerPID); /* * If someone has already notified this listener, we don't * bother modifying the table, but we do still send a SIGUSR2 * signal, just in case that backend missed the earlier signal * for some reason. It's OK to send the signal first, because * the other guy can't read pg_listener until we unlock it. */ if (kill(listenerPID, SIGUSR2) < 0) { /* * Get rid of pg_listener entry if it refers to a PID that * no longer exists. Presumably, that backend crashed * without deleting its pg_listener entries. This code * used to only delete the entry if errno==ESRCH, but as * far as I can see we should just do it for any failure * (certainly at least for EPERM too...) */ simple_heap_delete(lRel, &lTuple->t_self); } else if (listener->notification == 0) { int result; ItemPointerData update_ctid; TransactionId update_xmax; rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl); /* * We cannot use simple_heap_update here because the tuple * could have been modified by an uncommitted transaction; * specifically, since UNLISTEN releases exclusive lock on * the table before commit, the other guy could already have * tried to unlisten. There are no other cases where we * should be able to see an uncommitted update or delete. * Therefore, our response to a HeapTupleBeingUpdated result * is just to ignore it. We do *not* wait for the other * guy to commit --- that would risk deadlock, and we don't * want to block while holding the table lock anyway for * performance reasons. We also ignore HeapTupleUpdated, * which could occur if the other guy commits between our * heap_getnext and heap_update calls. */ result = heap_update(lRel, &lTuple->t_self, rTuple, &update_ctid, &update_xmax, GetCurrentCommandId(), SnapshotAny, false /* no wait for commit */); switch (result) { case HeapTupleSelfUpdated: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; case HeapTupleMayBeUpdated: /* done successfully */ #ifdef NOT_USED /* currently there are no indexes */ CatalogUpdateIndexes(lRel, rTuple); #endif break; case HeapTupleBeingUpdated: /* ignore uncommitted tuples */ break; case HeapTupleUpdated: /* ignore just-committed tuples */ break; default: elog(ERROR, "unrecognized heap_update status: %u", result); break; } } } } heap_endscan(scan); /* * We do NOT release the lock on pg_listener here; we need to hold it * until end of transaction (which is about to happen, anyway) to * ensure that notified backends see our tuple updates when they look. * Else they might disregard the signal, which would make the * application programmer very unhappy. */ heap_close(lRel, NoLock); ClearPendingNotifies(); if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: done"); }
/* ---------------------------------------------------------------- * UpdateIndexRelation * ---------------------------------------------------------------- */ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo *indexInfo, Oid *classOids, bool primary) { int16 indkey[INDEX_MAX_KEYS]; Oid indclass[INDEX_MAX_KEYS]; Datum exprsDatum; Datum predDatum; Datum values[Natts_pg_index]; char nulls[Natts_pg_index]; Relation pg_index; HeapTuple tuple; int i; /* * Copy the index key and opclass info into zero-filled vectors */ MemSet(indkey, 0, sizeof(indkey)); MemSet(indclass, 0, sizeof(indclass)); for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { indkey[i] = indexInfo->ii_KeyAttrNumbers[i]; indclass[i] = classOids[i]; } /* * Convert the index expressions (if any) to a text datum */ if (indexInfo->ii_Expressions != NIL) { char *exprsString; exprsString = nodeToString(indexInfo->ii_Expressions); exprsDatum = DirectFunctionCall1(textin, CStringGetDatum(exprsString)); pfree(exprsString); } else exprsDatum = (Datum) 0; /* * Convert the index predicate (if any) to a text datum */ if (indexInfo->ii_Predicate != NIL) { char *predString; predString = nodeToString(indexInfo->ii_Predicate); predDatum = DirectFunctionCall1(textin, CStringGetDatum(predString)); pfree(predString); } else predDatum = (Datum) 0; /* * open the system catalog index relation */ pg_index = heap_openr(IndexRelationName, RowExclusiveLock); /* * Build a pg_index tuple */ MemSet(nulls, ' ', sizeof(nulls)); values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid); values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid); values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey); values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass); values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs); values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique); values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary); values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false); values[Anum_pg_index_indexprs - 1] = exprsDatum; if (exprsDatum == (Datum) 0) nulls[Anum_pg_index_indexprs - 1] = 'n'; values[Anum_pg_index_indpred - 1] = predDatum; if (predDatum == (Datum) 0) nulls[Anum_pg_index_indpred - 1] = 'n'; tuple = heap_formtuple(RelationGetDescr(pg_index), values, nulls); /* * insert the tuple into the pg_index catalog */ simple_heap_insert(pg_index, tuple); /* update the indexes on pg_index */ CatalogUpdateIndexes(pg_index, tuple); /* * close the relation and free the tuple */ heap_close(pg_index, RowExclusiveLock); heap_freetuple(tuple); }
/* -------------------------------- * ReverifyMyDatabase * * Since we are forced to fetch the database OID out of pg_database without * benefit of locking or transaction ID checking (see utils/misc/database.c), * we might have gotten a wrong answer. Or, we might have attached to a * database that's in process of being destroyed by destroydb(). This * routine is called after we have all the locking and other infrastructure * running --- now we can check that we are really attached to a valid * database. * * In reality, if destroydb() is running in parallel with our startup, * it's pretty likely that we will have failed before now, due to being * unable to read some of the system tables within the doomed database. * This routine just exists to make *sure* we have not started up in an * invalid database. If we quit now, we should have managed to avoid * creating any serious problems. * * This is also a handy place to fetch the database encoding info out * of pg_database. * * To avoid having to read pg_database more times than necessary * during session startup, this place is also fitting to set up any * database-specific configuration variables. * -------------------------------- */ static void ReverifyMyDatabase(const char *name) { Relation pgdbrel; HeapScanDesc pgdbscan; ScanKeyData key; HeapTuple tup; Form_pg_database dbform; /* * Because we grab AccessShareLock here, we can be sure that destroydb * is not running in parallel with us (any more). */ pgdbrel = heap_openr(DatabaseRelationName, AccessShareLock); ScanKeyEntryInitialize(&key, 0, Anum_pg_database_datname, F_NAMEEQ, NameGetDatum(name)); pgdbscan = heap_beginscan(pgdbrel, SnapshotNow, 1, &key); tup = heap_getnext(pgdbscan, ForwardScanDirection); if (!HeapTupleIsValid(tup) || HeapTupleGetOid(tup) != MyDatabaseId) { /* OOPS */ heap_close(pgdbrel, AccessShareLock); /* * The only real problem I could have created is to load dirty * buffers for the dead database into shared buffer cache; if I * did, some other backend will eventually try to write them and * die in mdblindwrt. Flush any such pages to forestall trouble. */ DropBuffers(MyDatabaseId); /* Now I can commit hara-kiri with a clear conscience... */ ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\", OID %u, has disappeared from pg_database", name, MyDatabaseId))); } /* * Also check that the database is currently allowing connections. * (We do not enforce this in standalone mode, however, so that there is * a way to recover from "UPDATE pg_database SET datallowconn = false;") */ dbform = (Form_pg_database) GETSTRUCT(tup); if (IsUnderPostmaster && !dbform->datallowconn) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("database \"%s\" is not currently accepting connections", name))); /* * OK, we're golden. Only other to-do item is to save the encoding * info out of the pg_database tuple. */ SetDatabaseEncoding(dbform->encoding); /* Record it as a GUC internal option, too */ SetConfigOption("server_encoding", GetDatabaseEncodingName(), PGC_INTERNAL, PGC_S_OVERRIDE); /* If we have no other source of client_encoding, use server encoding */ SetConfigOption("client_encoding", GetDatabaseEncodingName(), PGC_BACKEND, PGC_S_DEFAULT); /* * Set up database-specific configuration variables. */ if (IsUnderPostmaster) { Datum datum; bool isnull; datum = heap_getattr(tup, Anum_pg_database_datconfig, RelationGetDescr(pgdbrel), &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); ProcessGUCArray(a, PGC_S_DATABASE); } } heap_endscan(pgdbscan); heap_close(pgdbrel, AccessShareLock); }
/* * cluster * * Check that the relation is a relation in the appropriate user * ACL. I will use the same security that limits users on the * renamerel() function. * * Check that the index specified is appropriate for the task * ( ie it's an index over this relation ). This is trickier. * * Create a list of all the other indicies on this relation. Because * the cluster will wreck all the tids, I'll need to destroy bogus * indicies. The user will have to re-create them. Not nice, but * I'm not a nice guy. The alternative is to try some kind of post * destroy re-build. This may be possible. I'll check out what the * index create functiond want in the way of paramaters. On the other * hand, re-creating n indicies may blow out the space. * * Create new (temporary) relations for the base heap and the new * index. * * Exclusively lock the relations. * * Create new clustered index and base heap relation. * */ void cluster(char oldrelname[], char oldindexname[]) { Oid OIDOldHeap, OIDOldIndex, OIDNewHeap; Relation OldHeap, OldIndex; Relation NewHeap; char *NewIndexName; char *szNewHeapName; /* * * I'm going to force all checking back into the commands.c function. * * Get the list if indicies for this relation. If the index we want * is among them, do not add it to the 'kill' list, as it will be * handled by the 'clean up' code which commits this transaction. * * I'm not using the SysCache, because this will happen but * once, and the slow way is the sure way in this case. * */ /* * Like vacuum, cluster spans transactions, so I'm going to handle it in * the same way. */ /* matches the StartTransaction in PostgresMain() */ OldHeap = heap_openr(oldrelname); if (!RelationIsValid(OldHeap)) { elog(WARN, "cluster: unknown relation: \"%-.*s\"", NAMEDATALEN, oldrelname); } OIDOldHeap = OldHeap->rd_id; /* Get OID for the index scan */ OldIndex=index_openr(oldindexname);/* Open old index relation */ if (!RelationIsValid(OldIndex)) { elog(WARN, "cluster: unknown index: \"%-.*s\"", NAMEDATALEN, oldindexname); } OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */ heap_close(OldHeap); index_close(OldIndex); /* * I need to build the copies of the heap and the index. The Commit() * between here is *very* bogus. If someone is appending stuff, they will * get the lock after being blocked and add rows which won't be present in * the new table. Bleagh! I'd be best to try and ensure that no-one's * in the tables for the entire duration of this process with a pg_vlock. */ NewHeap = copy_heap(OIDOldHeap); OIDNewHeap = NewHeap->rd_id; szNewHeapName = pstrdup(NewHeap->rd_rel->relname.data); /* Need to do this to make the new heap visible. */ CommandCounterIncrement(); rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex); /* Need to do this to make the new heap visible. */ CommandCounterIncrement(); /* can't be found in the SysCache. */ copy_index(OIDOldIndex, OIDNewHeap); /* No contention with the old */ /* * make this really happen. Flush all the buffers. */ CommitTransactionCommand(); StartTransactionCommand(); /* * Questionable bit here. Because the renamerel destroys all trace of the * pre-existing relation, I'm going to Destroy old, and then rename new * to old. If this fails, it fails, and you lose your old. Tough - say * I. Have good backups! */ /* Here lies the bogosity. The RelationNameGetRelation returns a bad list of TupleDescriptors. Damn. Can't work out why this is. */ heap_destroy(oldrelname); /* AAAAAAAAGH!! */ CommandCounterIncrement(); /* * The Commit flushes all palloced memory, so I have to grab the * New stuff again. This is annoying, but oh heck! */ /* renamerel(szNewHeapName.data, oldrelname); TypeRename(&szNewHeapName, &szOldRelName); sprintf(NewIndexName.data, "temp_%x", OIDOldIndex); renamerel(NewIndexName.data, szOldIndexName.data); */ NewIndexName = palloc(NAMEDATALEN+1); /* XXX */ sprintf(NewIndexName, "temp_%x", OIDOldIndex); renamerel(NewIndexName, oldindexname); }
/* *-------------------------------------------------------------- * Async_Listen * * This is executed by the SQL listen command. * * Register a backend (identified by its Unix PID) as listening * on the specified relation. * * Results: * XXX * * Side effects: * pg_listener is updated. * *-------------------------------------------------------------- */ void Async_Listen(char *relname, int pid) { Relation lRel; HeapScanDesc scan; HeapTuple tuple; Datum values[Natts_pg_listener]; char nulls[Natts_pg_listener]; int i; bool alreadyListener = false; if (Trace_notify) elog(DEBUG1, "Async_Listen(%s,%d)", relname, pid); lRel = heap_openr(ListenerRelationName, ExclusiveLock); /* Detect whether we are already listening on this relname */ scan = heap_beginscan(lRel, SnapshotNow, 0, (ScanKey) NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(tuple); if (listener->listenerpid == pid && strncmp(NameStr(listener->relname), relname, NAMEDATALEN) == 0) { alreadyListener = true; /* No need to scan the rest of the table */ break; } } heap_endscan(scan); if (alreadyListener) { heap_close(lRel, ExclusiveLock); return; } /* * OK to insert a new tuple */ for (i = 0; i < Natts_pg_listener; i++) { nulls[i] = ' '; values[i] = PointerGetDatum(NULL); } i = 0; values[i++] = (Datum) relname; values[i++] = (Datum) pid; values[i++] = (Datum) 0; /* no notifies pending */ tuple = heap_formtuple(RelationGetDescr(lRel), values, nulls); simple_heap_insert(lRel, tuple); #ifdef NOT_USED /* currently there are no indexes */ CatalogUpdateIndexes(lRel, tuple); #endif heap_freetuple(tuple); heap_close(lRel, ExclusiveLock); /* * now that we are listening, make sure we will unlisten before dying. */ if (!unlistenExitRegistered) { on_shmem_exit(Async_UnlistenOnExit, 0); unlistenExitRegistered = true; } }
/* * index_drop * * NOTE: this routine should now only be called through performDeletion(), * else associated dependencies won't be cleaned up. */ void index_drop(Oid indexId) { Oid heapId; Relation userHeapRelation; Relation userIndexRelation; Relation indexRelation; HeapTuple tuple; int i; Assert(OidIsValid(indexId)); /* * To drop an index safely, we must grab exclusive lock on its parent * table; otherwise there could be other backends using the index! * Exclusive lock on the index alone is insufficient because another * backend might be in the midst of devising a query plan that will * use the index. The parser and planner take care to hold an * appropriate lock on the parent table while working, but having them * hold locks on all the indexes too seems overly complex. We do grab * exclusive lock on the index too, just to be safe. Both locks must * be held till end of transaction, else other backends will still see * this index in pg_index. */ heapId = IndexGetRelation(indexId); userHeapRelation = heap_open(heapId, AccessExclusiveLock); userIndexRelation = index_open(indexId); LockRelation(userIndexRelation, AccessExclusiveLock); /* * fix RELATION relation */ DeleteRelationTuple(indexId); /* * fix ATTRIBUTE relation */ DeleteAttributeTuples(indexId); /* * fix INDEX relation */ indexRelation = heap_openr(IndexRelationName, RowExclusiveLock); tuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexId), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for index %u", indexId); simple_heap_delete(indexRelation, &tuple->t_self); ReleaseSysCache(tuple); heap_close(indexRelation, RowExclusiveLock); /* * flush buffer cache and physically remove the file */ i = FlushRelationBuffers(userIndexRelation, (BlockNumber) 0); if (i < 0) elog(ERROR, "FlushRelationBuffers returned %d", i); smgrunlink(DEFAULT_SMGR, userIndexRelation); /* * We are presently too lazy to attempt to compute the new correct * value of relhasindex (the next VACUUM will fix it if necessary). So * there is no need to update the pg_class tuple for the owning * relation. But we must send out a shared-cache-inval notice on the * owning relation to ensure other backends update their relcache * lists of indexes. */ CacheInvalidateRelcache(heapId); /* * Close rels, but keep locks */ index_close(userIndexRelation); heap_close(userHeapRelation, NoLock); RelationForgetRelation(indexId); }
/* * OperatorShellMake * Make a "shell" entry for a not-yet-existing operator. */ static Oid OperatorShellMake(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId) { Relation pg_operator_desc; Oid operatorObjectId; int i; HeapTuple tup; Datum values[Natts_pg_operator]; char nulls[Natts_pg_operator]; NameData oname; TupleDesc tupDesc; /* * validate operator name */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_operator; ++i) { nulls[i] = ' '; values[i] = (Datum) NULL; /* redundant, but safe */ } /* * initialize values[] with the operator name and input data types. * Note that oprcode is set to InvalidOid, indicating it's a shell. */ i = 0; namestrcpy(&oname, operatorName); values[i++] = NameGetDatum(&oname); /* oprname */ values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */ values[i++] = Int32GetDatum(GetUserId()); /* oprowner */ values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */ values[i++] = BoolGetDatum(false); /* oprcanhash */ values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */ values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprresult */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcom */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprnegate */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprlsortop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrsortop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprltcmpop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprgtcmpop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcode */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrest */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprjoin */ /* * open pg_operator */ pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock); tupDesc = pg_operator_desc->rd_att; /* * create a new operator tuple */ tup = heap_formtuple(tupDesc, values, nulls); /* * insert our "shell" operator tuple */ operatorObjectId = simple_heap_insert(pg_operator_desc, tup); CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ makeOperatorDependencies(tup, RelationGetRelid(pg_operator_desc)); heap_freetuple(tup); /* * close the operator relation and return the oid. */ heap_close(pg_operator_desc, RowExclusiveLock); return operatorObjectId; }
/* * OperatorCreate * * "X" indicates an optional argument (i.e. one that can be NULL or 0) * operatorName name for new operator * operatorNamespace namespace for new operator * leftTypeId X left type ID * rightTypeId X right type ID * procedureName procedure for operator * commutatorName X commutator operator * negatorName X negator operator * restrictionName X restriction sel. procedure * joinName X join sel. procedure * canHash hash join can be used with this operator * leftSortName X left sort operator (for merge join) * rightSortName X right sort operator (for merge join) * ltCompareName X L<R compare operator (for merge join) * gtCompareName X L>R compare operator (for merge join) * * This routine gets complicated because it allows the user to * specify operators that do not exist. For example, if operator * "op" is being defined, the negator operator "negop" and the * commutator "commop" can also be defined without specifying * any information other than their names. Since in order to * add "op" to the PG_OPERATOR catalog, all the Oid's for these * operators must be placed in the fields of "op", a forward * declaration is done on the commutator and negator operators. * This is called creating a shell, and its main effect is to * create a tuple in the PG_OPERATOR catalog with minimal * information about the operator (just its name and types). * Forward declaration is used only for this purpose, it is * not available to the user as it is for type definition. * * Algorithm: * * check if operator already defined * if so, but oprcode is null, save the Oid -- we are filling in a shell * otherwise error * get the attribute types from relation descriptor for pg_operator * assign values to the fields of the operator: * operatorName * owner id (simply the user id of the caller) * operator "kind" either "b" for binary or "l" for left unary * canHash boolean * leftTypeObjectId -- type must already be defined * rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified * resultType -- defer this, since it must be determined from * the pg_procedure catalog * commutatorObjectId -- if this is NULL, enter ObjectId=0 * else if this already exists, enter its ObjectId * else if this does not yet exist, and is not * the same as the main operatorName, then create * a shell and enter the new ObjectId * else if this does not exist but IS the same * name & types as the main operator, set the ObjectId=0. * (We are creating a self-commutating operator.) * The link will be fixed later by OperatorUpd. * negatorObjectId -- same as for commutatorObjectId * leftSortObjectId -- same as for commutatorObjectId * rightSortObjectId -- same as for commutatorObjectId * operatorProcedure -- must access the pg_procedure catalog to get the * ObjectId of the procedure that actually does the operator * actions this is required. Do a lookup to find out the * return type of the procedure * restrictionProcedure -- must access the pg_procedure catalog to get * the ObjectId but this is optional * joinProcedure -- same as restrictionProcedure * now either insert or replace the operator into the pg_operator catalog * if the operator shell is being filled in * access the catalog in order to get a valid buffer * create a tuple using ModifyHeapTuple * get the t_self from the modified tuple and call RelationReplaceHeapTuple * else if a new operator is being created * create a tuple using heap_formtuple * call simple_heap_insert */ void OperatorCreate(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, List *procedureName, List *commutatorName, List *negatorName, List *restrictionName, List *joinName, bool canHash, List *leftSortName, List *rightSortName, List *ltCompareName, List *gtCompareName) { Relation pg_operator_desc; HeapTuple tup; char nulls[Natts_pg_operator]; char replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; bool operatorAlreadyDefined; Oid procOid; Oid operResultType; Oid commutatorId, negatorId, leftSortId, rightSortId, ltCompareId, gtCompareId, restOid, joinOid; bool selfCommutator = false; Oid typeId[FUNC_MAX_ARGS]; int nargs; NameData oname; TupleDesc tupDesc; int i; /* * Sanity checks */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); if (!OidIsValid(leftTypeId) && !OidIsValid(rightTypeId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("at least one of leftarg or rightarg must be specified"))); if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) { /* If it's not a binary op, these things mustn't be set: */ if (commutatorName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have commutators"))); if (joinName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have join selectivity"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can hash"))); if (leftSortName || rightSortName || ltCompareName || gtCompareName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can merge join"))); } operatorObjectId = OperatorGet(operatorName, operatorNamespace, leftTypeId, rightTypeId, &operatorAlreadyDefined); if (operatorAlreadyDefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("operator %s already exists", operatorName))); /* * At this point, if operatorObjectId is not InvalidOid then we are * filling in a previously-created shell. */ /* * Look up registered procedures -- find the return type of * procedureName to place in "result" field. Do this before shells are * created so we don't have to worry about deleting them later. */ MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid)); if (!OidIsValid(leftTypeId)) { typeId[0] = rightTypeId; nargs = 1; } else if (!OidIsValid(rightTypeId)) { typeId[0] = leftTypeId; nargs = 1; } else { typeId[0] = leftTypeId; typeId[1] = rightTypeId; nargs = 2; } procOid = LookupFuncName(procedureName, nargs, typeId, false); operResultType = get_func_rettype(procOid); /* * find restriction estimator */ if (restrictionName) { MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid)); typeId[0] = INTERNALOID; /* Query */ typeId[1] = OIDOID; /* operator OID */ typeId[2] = INTERNALOID; /* args list */ typeId[3] = INT4OID; /* varRelid */ restOid = LookupFuncName(restrictionName, 4, typeId, false); } else restOid = InvalidOid; /* * find join estimator */ if (joinName) { MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid)); typeId[0] = INTERNALOID; /* Query */ typeId[1] = OIDOID; /* operator OID */ typeId[2] = INTERNALOID; /* args list */ typeId[3] = INT2OID; /* jointype */ joinOid = LookupFuncName(joinName, 4, typeId, false); } else joinOid = InvalidOid; /* * set up values in the operator tuple */ for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) NULL; replaces[i] = 'r'; nulls[i] = ' '; } i = 0; namestrcpy(&oname, operatorName); values[i++] = NameGetDatum(&oname); /* oprname */ values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */ values[i++] = Int32GetDatum(GetUserId()); /* oprowner */ values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */ values[i++] = BoolGetDatum(canHash); /* oprcanhash */ values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */ values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */ values[i++] = ObjectIdGetDatum(operResultType); /* oprresult */ /* * Set up the other operators. If they do not currently exist, create * shells in order to get ObjectId's. */ if (commutatorName) { /* commutator has reversed arg types */ commutatorId = get_other_operator(commutatorName, rightTypeId, leftTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, true); /* * self-linkage to this operator; will fix below. Note that only * self-linkage for commutation makes sense. */ if (!OidIsValid(commutatorId)) selfCommutator = true; } else commutatorId = InvalidOid; values[i++] = ObjectIdGetDatum(commutatorId); /* oprcom */ if (negatorName) { /* negator has same arg types */ negatorId = get_other_operator(negatorName, leftTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); } else negatorId = InvalidOid; values[i++] = ObjectIdGetDatum(negatorId); /* oprnegate */ if (leftSortName) { /* left sort op takes left-side data type */ leftSortId = get_other_operator(leftSortName, leftTypeId, leftTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); } else leftSortId = InvalidOid; values[i++] = ObjectIdGetDatum(leftSortId); /* oprlsortop */ if (rightSortName) { /* right sort op takes right-side data type */ rightSortId = get_other_operator(rightSortName, rightTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); } else rightSortId = InvalidOid; values[i++] = ObjectIdGetDatum(rightSortId); /* oprrsortop */ if (ltCompareName) { /* comparator has same arg types */ ltCompareId = get_other_operator(ltCompareName, leftTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); } else ltCompareId = InvalidOid; values[i++] = ObjectIdGetDatum(ltCompareId); /* oprltcmpop */ if (gtCompareName) { /* comparator has same arg types */ gtCompareId = get_other_operator(gtCompareName, leftTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); } else gtCompareId = InvalidOid; values[i++] = ObjectIdGetDatum(gtCompareId); /* oprgtcmpop */ values[i++] = ObjectIdGetDatum(procOid); /* oprcode */ values[i++] = ObjectIdGetDatum(restOid); /* oprrest */ values[i++] = ObjectIdGetDatum(joinOid); /* oprjoin */ pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock); /* * If we are adding to an operator shell, update; else insert */ if (operatorObjectId) { tup = SearchSysCacheCopy(OPEROID, ObjectIdGetDatum(operatorObjectId), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for operator %u", operatorObjectId); tup = heap_modifytuple(tup, pg_operator_desc, values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); } else { tupDesc = pg_operator_desc->rd_att; tup = heap_formtuple(tupDesc, values, nulls); operatorObjectId = simple_heap_insert(pg_operator_desc, tup); } /* Must update the indexes in either case */ CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ makeOperatorDependencies(tup, RelationGetRelid(pg_operator_desc)); heap_close(pg_operator_desc, RowExclusiveLock); /* * If a commutator and/or negator link is provided, update the other * operator(s) to point at this one, if they don't already have a * link. This supports an alternate style of operator definition * wherein the user first defines one operator without giving negator * or commutator, then defines the other operator of the pair with the * proper commutator or negator attribute. That style doesn't require * creation of a shell, and it's the only style that worked right * before Postgres version 6.5. This code also takes care of the * situation where the new operator is its own commutator. */ if (selfCommutator) commutatorId = operatorObjectId; if (OidIsValid(commutatorId) || OidIsValid(negatorId)) OperatorUpd(operatorObjectId, commutatorId, negatorId); }
/* * -------------------------------------------------------------- * ProcessIncomingNotify * * Deal with arriving NOTIFYs from other backends. * This is called either directly from the SIGUSR2 signal handler, * or the next time control reaches the outer idle loop. * Scan pg_listener for arriving notifies, report them to my front end, * and clear the notification field in pg_listener until next time. * * NOTE: since we are outside any transaction, we must create our own. * * Results: * XXX * * -------------------------------------------------------------- */ static void ProcessIncomingNotify(void) { Relation lRel; TupleDesc tdesc; ScanKeyData key[1]; HeapScanDesc scan; HeapTuple lTuple, rTuple; Datum value[Natts_pg_listener]; char repl[Natts_pg_listener], nulls[Natts_pg_listener]; if (Trace_notify) elog(DEBUG1, "ProcessIncomingNotify"); set_ps_display("async_notify"); notifyInterruptOccurred = 0; StartTransactionCommand(); lRel = heap_openr(ListenerRelationName, ExclusiveLock); tdesc = RelationGetDescr(lRel); /* Scan only entries with my listenerPID */ ScanKeyEntryInitialize(&key[0], 0, Anum_pg_listener_pid, F_INT4EQ, Int32GetDatum(MyProcPid)); scan = heap_beginscan(lRel, SnapshotNow, 1, key); /* Prepare data for rewriting 0 into notification field */ nulls[0] = nulls[1] = nulls[2] = ' '; repl[0] = repl[1] = repl[2] = ' '; repl[Anum_pg_listener_notify - 1] = 'r'; value[0] = value[1] = value[2] = (Datum) 0; value[Anum_pg_listener_notify - 1] = Int32GetDatum(0); while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(lTuple); char *relname = NameStr(listener->relname); int32 sourcePID = listener->notification; if (sourcePID != 0) { /* Notify the frontend */ if (Trace_notify) elog(DEBUG1, "ProcessIncomingNotify: received %s from %d", relname, (int) sourcePID); NotifyMyFrontEnd(relname, sourcePID); /* * Rewrite the tuple with 0 in notification column. * * simple_heap_update is safe here because no one else would * have tried to UNLISTEN us, so there can be no uncommitted * changes. */ rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl); simple_heap_update(lRel, &lTuple->t_self, rTuple); #ifdef NOT_USED /* currently there are no indexes */ CatalogUpdateIndexes(lRel, rTuple); #endif } } heap_endscan(scan); /* * We do NOT release the lock on pg_listener here; we need to hold it * until end of transaction (which is about to happen, anyway) to * ensure that other backends see our tuple updates when they look. * Otherwise, a transaction started after this one might mistakenly * think it doesn't need to send this backend a new NOTIFY. */ heap_close(lRel, NoLock); CommitTransactionCommand(); /* * Must flush the notify messages to ensure frontend gets them * promptly. */ pq_flush(); set_ps_display("idle"); if (Trace_notify) elog(DEBUG1, "ProcessIncomingNotify: done"); }
/* * OperatorUpd * * For a given operator, look up its negator and commutator operators. * If they are defined, but their negator and commutator fields * (respectively) are empty, then use the new operator for neg or comm. * This solves a problem for users who need to insert two new operators * which are the negator or commutator of each other. */ static void OperatorUpd(Oid baseId, Oid commId, Oid negId) { int i; Relation pg_operator_desc; HeapTuple tup; char nulls[Natts_pg_operator]; char replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) 0; replaces[i] = ' '; nulls[i] = ' '; } /* * check and update the commutator & negator, if necessary * * First make sure we can see them... */ CommandCounterIncrement(); pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock); tup = SearchSysCacheCopy(OPEROID, ObjectIdGetDatum(commId), 0, 0, 0); /* * if the commutator and negator are the same operator, do one update. * XXX this is probably useless code --- I doubt it ever makes sense * for commutator and negator to be the same thing... */ if (commId == negId) { if (HeapTupleIsValid(tup)) { Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); if (!OidIsValid(t->oprcom) || !OidIsValid(t->oprnegate)) { if (!OidIsValid(t->oprnegate)) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = 'r'; } if (!OidIsValid(t->oprcom)) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = 'r'; } tup = heap_modifytuple(tup, pg_operator_desc, values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); CatalogUpdateIndexes(pg_operator_desc, tup); } } heap_close(pg_operator_desc, RowExclusiveLock); return; } /* if commutator and negator are different, do two updates */ if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom))) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = 'r'; tup = heap_modifytuple(tup, pg_operator_desc, values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); CatalogUpdateIndexes(pg_operator_desc, tup); values[Anum_pg_operator_oprcom - 1] = (Datum) NULL; replaces[Anum_pg_operator_oprcom - 1] = ' '; } /* check and update the negator, if necessary */ tup = SearchSysCacheCopy(OPEROID, ObjectIdGetDatum(negId), 0, 0, 0); if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate))) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = 'r'; tup = heap_modifytuple(tup, pg_operator_desc, values, nulls, replaces); simple_heap_update(pg_operator_desc, &tup->t_self, tup); CatalogUpdateIndexes(pg_operator_desc, tup); } heap_close(pg_operator_desc, RowExclusiveLock); }
/* * AggregateCreate */ void AggregateCreate(const char *aggName, Oid aggNamespace, List *aggtransfnName, List *aggfinalfnName, Oid aggBaseType, Oid aggTransType, const char *agginitval) { Relation aggdesc; HeapTuple tup; char nulls[Natts_pg_aggregate]; Datum values[Natts_pg_aggregate]; Form_pg_proc proc; Oid transfn; Oid finalfn = InvalidOid; /* can be omitted */ Oid rettype; Oid finaltype; Oid fnArgs[FUNC_MAX_ARGS]; int nargs_transfn; Oid procOid; TupleDesc tupDesc; int i; ObjectAddress myself, referenced; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* * If transtype is polymorphic, basetype must be polymorphic also; * else we will have no way to deduce the actual transtype. */ if ((aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID) && !(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using \"anyarray\" or \"anyelement\" as " "transition type must have one of them as its base type."))); /* handle transfn */ MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggTransType; if (aggBaseType == ANYOID) nargs_transfn = 1; else { fnArgs[1] = aggBaseType; nargs_transfn = 2; } transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs, &rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) * must exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", NameListToString(aggtransfnName), format_type_be(aggTransType)))); tup = SearchSysCache(PROCOID, ObjectIdGetDatum(transfn), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GETSTRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial * transValue. */ if (proc->proisstrict && agginitval == NULL) { if (!IsBinaryCoercible(aggBaseType, aggTransType)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type"))); } ReleaseSysCache(tup); /* handle finalfn, if supplied */ if (aggfinalfnName) { MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggTransType; finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } Assert(OidIsValid(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, basetype * must be polymorphic also, else parser will fail to deduce result * type. (Note: given the previous test on transtype and basetype, * this cannot happen, unless someone has snuck a finalfn definition * into the catalogs that itself violates the rule against polymorphic * result with no polymorphic input.) */ if ((finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) && !(aggBaseType == ANYARRAYOID || aggBaseType == ANYELEMENTOID)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " "must have one of them as its base type."))); /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting * entry.) */ MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggBaseType; procOid = ProcedureCreate(aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ 0, "aggregate_dummy", /* placeholder proc */ "-", /* probin */ true, /* isAgg */ false, /* security invoker (currently not * definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not * needed for agg) */ 1, /* parameterCount */ fnArgs); /* parameterTypes */ /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = ' '; values[i] = (Datum) NULL; } values[Anum_pg_aggregate_aggfnoid - 1] = ObjectIdGetDatum(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn); values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn); values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = DirectFunctionCall1(textin, CStringGetDatum(agginitval)); else nulls[Anum_pg_aggregate_agginitval - 1] = 'n'; aggdesc = heap_openr(AggregateRelationName, RowExclusiveLock); tupDesc = aggdesc->rd_att; tup = heap_formtuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); CatalogUpdateIndexes(aggdesc, tup); heap_close(aggdesc, RowExclusiveLock); /* * Create dependencies for the aggregate (above and beyond those * already made by ProcedureCreate). Note: we don't need an explicit * dependency on aggTransType since we depend on it indirectly through * transfn. */ myself.classId = RelOid_pg_proc; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = RelOid_pg_proc; referenced.objectId = transfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* Depends on final function, if any */ if (OidIsValid(finalfn)) { referenced.classId = RelOid_pg_proc; referenced.objectId = finalfn; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } }
static void CatalogCacheInitializeCache(CatCache *cache) { Relation relation; MemoryContext oldcxt; TupleDesc tupdesc; int i; CatalogCacheInitializeCache_DEBUG2; /* * Open the relation without locking --- we only need the tupdesc, * which we assume will never change ... */ relation = heap_openr(cache->cc_relname, NoLock); Assert(RelationIsValid(relation)); /* * switch to the cache context so our allocations do not vanish at the * end of a transaction */ Assert(CacheMemoryContext != NULL); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * copy the relcache's tuple descriptor to permanent cache storage */ tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation)); /* * get the relation's OID and relisshared flag, too */ cache->cc_reloid = RelationGetRelid(relation); cache->cc_relisshared = RelationGetForm(relation)->relisshared; /* * return to the caller's memory context and close the rel */ MemoryContextSwitchTo(oldcxt); heap_close(relation, NoLock); CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys", cache->cc_relname, cache->cc_nkeys); /* * initialize cache's key information */ for (i = 0; i < cache->cc_nkeys; ++i) { Oid keytype; CatalogCacheInitializeCache_DEBUG2; if (cache->cc_key[i] > 0) keytype = tupdesc->attrs[cache->cc_key[i] - 1]->atttypid; else { if (cache->cc_key[i] != ObjectIdAttributeNumber) elog(FATAL, "only sys attr supported in caches is OID"); keytype = OIDOID; } GetCCHashEqFuncs(keytype, &cache->cc_hashfunc[i], &cache->cc_skey[i].sk_procedure); cache->cc_isname[i] = (keytype == NAMEOID); /* * Do equality-function lookup (we assume this won't need a * catalog lookup for any supported type) */ fmgr_info_cxt(cache->cc_skey[i].sk_procedure, &cache->cc_skey[i].sk_func, CacheMemoryContext); /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */ cache->cc_skey[i].sk_attno = cache->cc_key[i]; CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p", cache->cc_relname, i, cache); } /* * mark this cache fully initialized */ cache->cc_tupdesc = tupdesc; }