/* * Rename language */ void RenameLanguage(const char *oldname, const char *newname) { HeapTuple tup; Relation rel; rel = heap_open(LanguageRelationId, RowExclusiveLock); tup = SearchSysCacheCopy1(LANGNAME, CStringGetDatum(oldname)); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("language \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ if (SearchSysCacheExists1(LANGNAME, CStringGetDatum(newname))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("language \"%s\" already exists", newname))); /* must be owner of PL */ if (!pg_language_ownercheck(HeapTupleGetOid(tup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, oldname); /* rename */ namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); heap_close(rel, NoLock); heap_freetuple(tup); }
/** * @brief Setting the number of tuples. * @param relation_id relation id * @param num_tuples number of tuples */ void Bridge::SetNumberOfTuples(Oid relation_id, float num_tuples) { assert(relation_id); Relation pg_class_rel; HeapTuple tuple; Form_pg_class pgclass; // Open target table in exclusive mode pg_class_rel = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relation_id)); if (!HeapTupleIsValid(tuple)) { elog(DEBUG2, "cache lookup failed for relation %u", relation_id); } else { pgclass = (Form_pg_class)GETSTRUCT(tuple); pgclass->reltuples = (float4)num_tuples; pgclass->relpages = (int32)1; // update tuple simple_heap_update(pg_class_rel, &tuple->t_self, tuple); /* keep the catalog indexes up to date */ CatalogUpdateIndexes(pg_class_rel, tuple); heap_freetuple(tuple); } heap_close(pg_class_rel, RowExclusiveLock); }
/* * SetRelationRuleStatus * Set the value of the relation's relhasrules field in pg_class. * * 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) { Relation relationRelation; HeapTuple tuple; Form_pg_class classForm; /* * Find the tuple to update in pg_class, using syscache for the lookup. */ relationRelation = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relationId); classForm = (Form_pg_class) GETSTRUCT(tuple); if (classForm->relhasrules != relHasRules) { /* Do the update */ classForm->relhasrules = relHasRules; 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); }
/* * SetMatViewPopulatedState * Mark a materialized view as populated, or not. * * NOTE: caller must be holding an appropriate lock on the relation. */ void SetMatViewPopulatedState(Relation relation, bool newstate) { Relation pgrel; HeapTuple tuple; Assert(relation->rd_rel->relkind == RELKIND_MATVIEW); /* * Update relation's pg_class entry. Crucial side-effect: other backends * (and this one too!) are sent SI message to make them rebuild relcache * entries. */ pgrel = table_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(RelationGetRelid(relation))); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(relation)); ((Form_pg_class) GETSTRUCT(tuple))->relispopulated = newstate; CatalogTupleUpdate(pgrel, &tuple->t_self, tuple); heap_freetuple(tuple); table_close(pgrel, RowExclusiveLock); /* * Advance command counter to make the updated pg_class row locally * visible. */ CommandCounterIncrement(); }
/* * RenameTypeInternal * This renames a type, as well as any associated array type. * * Caller must have already checked privileges. * * Currently this is used for renaming table rowtypes and for * ALTER TYPE RENAME TO command. */ void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) { Relation pg_type_desc; HeapTuple tuple; Form_pg_type typ; Oid arrayOid; pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for type %u", typeOid); typ = (Form_pg_type) GETSTRUCT(tuple); /* We are not supposed to be changing schemas here */ Assert(typeNamespace == typ->typnamespace); arrayOid = typ->typarray; /* Just to give a more friendly error than unique-index violation */ if (SearchSysCacheExists2(TYPENAMENSP, CStringGetDatum(newTypeName), ObjectIdGetDatum(typeNamespace))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", newTypeName))); /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(typ->typname), newTypeName); simple_heap_update(pg_type_desc, &tuple->t_self, tuple); /* update the system catalog indexes */ CatalogUpdateIndexes(pg_type_desc, tuple); InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); heap_freetuple(tuple); heap_close(pg_type_desc, RowExclusiveLock); /* If the type has an array type, recurse to handle that */ if (OidIsValid(arrayOid)) { char *arrname = makeArrayTypeName(newTypeName, typeNamespace); RenameTypeInternal(arrayOid, arrname, typeNamespace); pfree(arrname); } }
/* * RenameConstraintById * Rename a constraint. * * Note: this isn't intended to be a user-exposed function; it doesn't check * permissions etc. Currently this is only invoked when renaming an index * that is associated with a constraint, but it's made a little more general * than that with the expectation of someday having ALTER TABLE RENAME * CONSTRAINT. */ void RenameConstraintById(Oid conId, const char *newname) { Relation conDesc; HeapTuple tuple; Form_pg_constraint con; conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(CONSTROID, ObjectIdGetDatum(conId)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for constraint %u", conId); con = (Form_pg_constraint) GETSTRUCT(tuple); /* * We need to check whether the name is already in use --- note that there * currently is not a unique index that would catch this. */ if (OidIsValid(con->conrelid) && ConstraintNameIsUsed(CONSTRAINT_RELATION, con->conrelid, con->connamespace, newname)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("constraint \"%s\" for relation \"%s\" already exists", newname, get_rel_name(con->conrelid)))); if (OidIsValid(con->contypid) && ConstraintNameIsUsed(CONSTRAINT_DOMAIN, con->contypid, con->connamespace, newname)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("constraint \"%s\" for domain %s already exists", newname, format_type_be(con->contypid)))); /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(con->conname), newname); simple_heap_update(conDesc, &tuple->t_self, tuple); /* update the system catalog indexes */ CatalogUpdateIndexes(conDesc, tuple); InvokeObjectPostAlterHook(ConstraintRelationId, conId, 0); heap_freetuple(tuple); heap_close(conDesc, RowExclusiveLock); }
/* * Rename conversion */ Oid RenameConversion(List *name, const char *newname) { Oid conversionOid; Oid namespaceOid; HeapTuple tup; Relation rel; AclResult aclresult; rel = heap_open(ConversionRelationId, RowExclusiveLock); conversionOid = get_conversion_oid(name, false); tup = SearchSysCacheCopy1(CONVOID, ObjectIdGetDatum(conversionOid)); 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 (SearchSysCacheExists2(CONNAMENSP, CStringGetDatum(newname), ObjectIdGetDatum(namespaceOid))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conversion \"%s\" already exists in schema \"%s\"", newname, get_namespace_name(namespaceOid)))); /* must be owner */ if (!pg_conversion_ownercheck(conversionOid, 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); return conversionOid; }
/** * @brief Get the pg class tuple * @param tuple relevant tuple if it exists, NULL otherwise */ HeapTuple Bridge::GetPGClassTupleForRelationOid(Oid relation_id) { LOG_WARN("Do not use bridge function(%s) in Peloton !!! ", __func__); Relation pg_class_rel; HeapTuple tuple = NULL; // Open pg_class table pg_class_rel = heap_open(RelationRelationId, AccessShareLock); // Search the pg_class table with given relation id tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relation_id)); if (!HeapTupleIsValid(tuple)) { elog(DEBUG2, "cache lookup failed for relation %u", relation_id); // Don't break here, we need to close heap and commit. } heap_close(pg_class_rel, AccessShareLock); return tuple; }
/* * update_default_partition_oid * * Update pg_partition_table.partdefid with a new default partition OID. */ void update_default_partition_oid(Oid parentId, Oid defaultPartId) { HeapTuple tuple; Relation pg_partitioned_table; Form_pg_partitioned_table part_table_form; pg_partitioned_table = table_open(PartitionedRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(PARTRELID, ObjectIdGetDatum(parentId)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for partition key of relation %u", parentId); part_table_form = (Form_pg_partitioned_table) GETSTRUCT(tuple); part_table_form->partdefid = defaultPartId; CatalogTupleUpdate(pg_partitioned_table, &tuple->t_self, tuple); heap_freetuple(tuple); table_close(pg_partitioned_table, RowExclusiveLock); }
/* * 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; bool nulls[Natts_pg_operator]; bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) 0; replaces[i] = false; nulls[i] = false; } /* * check and update the commutator & negator, if necessary * * We need a CommandCounterIncrement here in case of a self-commutator * operator: we'll need to update the tuple that we just inserted. */ CommandCounterIncrement(); pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId)); /* * 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] = true; } if (!OidIsValid(t->oprcom)) { values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprcom - 1] = true; } tup = heap_modify_tuple(tup, RelationGetDescr(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] = true; tup = heap_modify_tuple(tup, RelationGetDescr(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] = false; } /* check and update the negator, if necessary */ tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId)); if (HeapTupleIsValid(tup) && !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate))) { values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId); replaces[Anum_pg_operator_oprnegate - 1] = true; tup = heap_modify_tuple(tup, RelationGetDescr(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); }
/* * Delete a single constraint record. */ void RemoveConstraintById(Oid conId) { Relation conDesc; HeapTuple tup; Form_pg_constraint con; conDesc = heap_open(ConstraintRelationId, RowExclusiveLock); tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conId)); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for constraint %u", conId); con = (Form_pg_constraint) GETSTRUCT(tup); /* * Special processing depending on what the constraint is for. */ if (OidIsValid(con->conrelid)) { Relation rel; /* * If the constraint is for a relation, open and exclusive-lock the * relation it's for. */ rel = heap_open(con->conrelid, AccessExclusiveLock); /* * We need to update the relcheck count if it is a check constraint * being dropped. This update will force backends to rebuild relcache * entries when we commit. */ if (con->contype == CONSTRAINT_CHECK) { Relation pgrel; HeapTuple relTup; Form_pg_class classForm; pgrel = heap_open(RelationRelationId, RowExclusiveLock); relTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(con->conrelid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", con->conrelid); classForm = (Form_pg_class) GETSTRUCT(relTup); if (classForm->relchecks == 0) /* should not happen */ elog(ERROR, "relation \"%s\" has relchecks = 0", RelationGetRelationName(rel)); classForm->relchecks--; simple_heap_update(pgrel, &relTup->t_self, relTup); CatalogUpdateIndexes(pgrel, relTup); heap_freetuple(relTup); heap_close(pgrel, RowExclusiveLock); } /* Keep lock on constraint's rel until end of xact */ heap_close(rel, NoLock); } else if (OidIsValid(con->contypid)) { /* * XXX for now, do nothing special when dropping a domain constraint * * Probably there should be some form of locking on the domain type, * but we have no such concept at the moment. */ } else elog(ERROR, "constraint %u is not of a known type", conId); /* Fry the constraint itself */ simple_heap_delete(conDesc, &tup->t_self); /* Clean up */ ReleaseSysCache(tup); heap_close(conDesc, RowExclusiveLock); }
/* * OperatorUpd * * For a given operator, look up its negator and commutator operators. * When isDelete is false, update their negator and commutator fields to * point back to the given operator; when isDelete is true, update those * fields to no longer point back to the given operator. * * The !isDelete case solves a problem for users who need to insert two new * operators that are the negator or commutator of each other, while the * isDelete case is needed so as not to leave dangling OID links behind * after dropping an operator. */ void OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete) { Relation pg_operator_desc; HeapTuple tup; /* * If we're making an operator into its own commutator, then we need a * command-counter increment here, since we've just inserted the tuple * we're about to update. But when we're dropping an operator, we can * skip this because we're at the beginning of the command. */ if (!isDelete) CommandCounterIncrement(); /* Open the relation. */ pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); /* Get a writable copy of the commutator's tuple. */ if (OidIsValid(commId)) tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId)); else tup = NULL; /* Update the commutator's tuple if need be. */ if (HeapTupleIsValid(tup)) { Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); bool update_commutator = false; /* * Out of due caution, we only change the commutator's oprcom field if * it has the exact value we expected: InvalidOid when creating an * operator, or baseId when dropping one. */ if (isDelete && t->oprcom == baseId) { t->oprcom = InvalidOid; update_commutator = true; } else if (!isDelete && !OidIsValid(t->oprcom)) { t->oprcom = baseId; update_commutator = true; } /* If any columns were found to need modification, update tuple. */ if (update_commutator) { CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); /* * Do CCI to make the updated tuple visible. We must do this in * case the commutator is also the negator. (Which would be a * logic error on the operator definer's part, but that's not a * good reason to fail here.) We would need a CCI anyway in the * deletion case for a self-commutator with no negator. */ CommandCounterIncrement(); } } /* * Similarly find and update the negator, if any. */ if (OidIsValid(negId)) tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId)); else tup = NULL; if (HeapTupleIsValid(tup)) { Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup); bool update_negator = false; /* * Out of due caution, we only change the negator's oprnegate field if * it has the exact value we expected: InvalidOid when creating an * operator, or baseId when dropping one. */ if (isDelete && t->oprnegate == baseId) { t->oprnegate = InvalidOid; update_negator = true; } else if (!isDelete && !OidIsValid(t->oprnegate)) { t->oprnegate = baseId; update_negator = true; } /* If any columns were found to need modification, update tuple. */ if (update_negator) { CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); /* * In the deletion case, do CCI to make the updated tuple visible. * We must do this in case the operator is its own negator. (Which * would be a logic error on the operator definer's part, but * that's not a good reason to fail here.) */ if (isDelete) CommandCounterIncrement(); } } /* Close relation and release catalog lock. */ heap_close(pg_operator_desc, RowExclusiveLock); }
/* * 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 * procedureId procedure ID for operator * commutatorName X commutator operator * negatorName X negator operator * restrictionId X restriction selectivity procedure ID * joinId X join selectivity procedure ID * canMerge merge join can be used with this operator * canHash hash join can be used with this operator * * The caller should have validated properties and permissions for the * objects passed as OID references. We must handle the commutator and * negator operator references specially, however, since those need not * exist beforehand. * * 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. */ ObjectAddress OperatorCreate(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, Oid procedureId, List *commutatorName, List *negatorName, Oid restrictionId, Oid joinId, bool canMerge, bool canHash) { Relation pg_operator_desc; HeapTuple tup; bool isUpdate; bool nulls[Natts_pg_operator]; bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; bool operatorAlreadyDefined; Oid operResultType; Oid commutatorId, negatorId; bool selfCommutator = false; NameData oname; int i; ObjectAddress address; /* * 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))) { /* 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 (OidIsValid(joinId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have join selectivity"))); if (canMerge) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can merge join"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can hash"))); } operResultType = get_func_rettype(procedureId); if (operResultType != BOOLOID) { /* If it's not a boolean op, these things mustn't be set: */ if (negatorName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can have negators"))); if (OidIsValid(restrictionId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can have restriction selectivity"))); if (OidIsValid(joinId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can have join selectivity"))); if (canMerge) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can merge join"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only boolean operators can hash"))); } 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. Insist that the user own any * such shell. */ if (OidIsValid(operatorObjectId) && !pg_oper_ownercheck(operatorObjectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, operatorName); /* * 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); /* Permission check: must own other operator */ if (OidIsValid(commutatorId) && !pg_oper_ownercheck(commutatorId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(commutatorName)); /* * 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; if (negatorName) { /* negator has same arg types */ negatorId = get_other_operator(negatorName, leftTypeId, rightTypeId, operatorName, operatorNamespace, leftTypeId, rightTypeId, false); /* Permission check: must own other operator */ if (OidIsValid(negatorId) && !pg_oper_ownercheck(negatorId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(negatorName)); } else negatorId = InvalidOid; /* * set up values in the operator tuple */ for (i = 0; i < Natts_pg_operator; ++i) { values[i] = (Datum) NULL; replaces[i] = true; nulls[i] = false; } namestrcpy(&oname, operatorName); values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname); values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace); values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge); values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash); values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId); values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId); values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType); values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId); values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId); values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId); values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId); values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId); pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); /* * If we are replacing an operator shell, update; else insert */ if (operatorObjectId) { isUpdate = true; tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operatorObjectId)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for operator %u", operatorObjectId); tup = heap_modify_tuple(tup, RelationGetDescr(pg_operator_desc), values, nulls, replaces); CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup); } else { isUpdate = false; tup = heap_form_tuple(RelationGetDescr(pg_operator_desc), values, nulls); operatorObjectId = CatalogTupleInsert(pg_operator_desc, tup); } /* Add dependencies for the entry */ address = makeOperatorDependencies(tup, isUpdate); /* Post creation hook for new operator */ InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0); 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 alternative 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, false); return address; }
/* * create_toast_table --- internal workhorse * * rel is already opened and locked * toastOid and toastIndexOid are normally InvalidOid, but during * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, LOCKMODE lockmode, bool check) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; TupleDesc tupdesc; bool shared_relation; bool mapped_relation; Relation toast_rel; Relation class_rel; Oid toast_relid; Oid toast_typid = InvalidOid; Oid namespaceid; char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid collationObjectId[2]; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, toastobject; /* * Toast table is shared if and only if its parent is. * * We cannot allow toasting a shared relation after initdb (because * there's no way to mark it toasted in other databases' pg_class). */ shared_relation = rel->rd_rel->relisshared; if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot be toasted after initdb"))); /* It's mapped if and only if its parent is, too */ mapped_relation = RelationIsMapped(rel); /* * Is it already toasted? */ if (rel->rd_rel->reltoastrelid != InvalidOid) return false; /* * Check to see whether the table actually needs a TOAST table. */ if (!IsBinaryUpgrade) { /* Normal mode, normal check */ if (!needs_toast_table(rel)) return false; } else { /* * In binary-upgrade mode, create a TOAST table if and only if * pg_upgrade told us to (ie, a TOAST table OID has been provided). * * This indicates that the old cluster had a TOAST table for the * current table. We must create a TOAST table to receive the old * TOAST file, even if the table seems not to need one. * * Contrariwise, if the old cluster did not have a TOAST table, we * should be able to get along without one even if the new version's * needs_toast_table rules suggest we should have one. There is a lot * of daylight between where we will create a TOAST table and where * one is really necessary to avoid failures, so small cross-version * differences in the when-to-create heuristic shouldn't be a problem. * If we tried to create a TOAST table anyway, we would have the * problem that it might take up an OID that will conflict with some * old-cluster table we haven't seen yet. */ if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid) || !OidIsValid(binary_upgrade_next_toast_pg_type_oid)) return false; } /* * If requested check lockmode is sufficient. This is a cross check in * case of errors or conflicting decisions in earlier code. */ if (check && lockmode != AccessExclusiveLock) elog(ERROR, "AccessExclusiveLock required to add toast table."); /* * Create the toast table and its index */ snprintf(toast_relname, sizeof(toast_relname), "pg_toast_%u", relOid); snprintf(toast_idxname, sizeof(toast_idxname), "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "chunk_data", BYTEAOID, -1, 0); /* * Ensure that the toast table doesn't itself get toasted, or we'll be * toast :-(. This is essential for chunk_data because type bytea is * toastable; hit the other two just to be sure. */ TupleDescAttr(tupdesc, 0)->attstorage = 'p'; TupleDescAttr(tupdesc, 1)->attstorage = 'p'; TupleDescAttr(tupdesc, 2)->attstorage = 'p'; /* * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ if (isTempOrTempToastNamespace(rel->rd_rel->relnamespace)) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; /* * Use binary-upgrade override for pg_type.oid, if supplied. We might be * in the post-schema-restore phase where we are doing ALTER TABLE to * create TOAST tables that didn't exist in the old cluster. */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid)) { toast_typid = binary_upgrade_next_toast_pg_type_oid; binary_upgrade_next_toast_pg_type_oid = InvalidOid; } toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, toastOid, toast_typid, InvalidOid, rel->rd_rel->relowner, tupdesc, NIL, RELKIND_TOASTVALUE, rel->rd_rel->relpersistence, shared_relation, mapped_relation, ONCOMMIT_NOOP, reloptions, false, true, true, InvalidOid, NULL); Assert(toast_relid != InvalidOid); /* make the toast relation visible, else heap_open will fail */ CommandCounterIncrement(); /* ShareLock is not really needed here, but take it anyway */ toast_rel = heap_open(toast_relid, ShareLock); /* * Create unique index on chunk_id, chunk_seq. * * NOTE: the normal TOAST access routines could actually function with a * single-column index on chunk_id only. However, the slice access * routines use both columns for faster access to an individual chunk. In * addition, we want it to be unique as a check against the possibility of * duplicate TOAST chunk OIDs. The index might also be a little more * efficient this way, since btree isn't all that happy with large numbers * of equal keys. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_NumIndexKeyAttrs = 2; indexInfo->ii_IndexAttrNumbers[0] = 1; indexInfo->ii_IndexAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; indexInfo->ii_ParallelWorkers = 0; indexInfo->ii_Am = BTREE_AM_OID; indexInfo->ii_AmCache = NULL; indexInfo->ii_Context = CurrentMemoryContext; collationObjectId[0] = InvalidOid; collationObjectId[1] = InvalidOid; classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; coloptions[0] = 0; coloptions[1] = 0; index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid, InvalidOid, InvalidOid, indexInfo, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, collationObjectId, classObjectId, coloptions, (Datum) 0, INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL); heap_close(toast_rel, NoLock); /* * Store the toast table's OID in the parent relation's pg_class row */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", relOid); ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid; if (!IsBootstrapProcessingMode()) { /* normal case, use a transactional update */ CatalogTupleUpdate(class_rel, &reltup->t_self, reltup); } else { /* While bootstrapping, we cannot UPDATE, so overwrite in-place */ heap_inplace_update(class_rel, reltup); } heap_freetuple(reltup); heap_close(class_rel, RowExclusiveLock); /* * Register dependency from the toast table to the master, so that the * toast table will be deleted if the master is. Skip this in bootstrap * mode. */ if (!IsBootstrapProcessingMode()) { baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectId = toast_relid; toastobject.objectSubId = 0; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } /* * Make changes visible */ CommandCounterIncrement(); return true; }
/* * PgxcClassAlter * Modify a pgxc_class entry with given data */ void PgxcClassAlter(Oid pcrelid, char pclocatortype, int pcattnum, int pchashalgorithm, int pchashbuckets, int numnodes, Oid *nodes, PgxcClassAlterType type) { Relation rel; HeapTuple oldtup, newtup; oidvector *nodes_array; Datum new_record[Natts_pgxc_class]; bool new_record_nulls[Natts_pgxc_class]; bool new_record_repl[Natts_pgxc_class]; Assert(OidIsValid(pcrelid)); rel = heap_open(PgxcClassRelationId, RowExclusiveLock); oldtup = SearchSysCacheCopy1(PGXCCLASSRELID, ObjectIdGetDatum(pcrelid)); if (!HeapTupleIsValid(oldtup)) /* should not happen */ elog(ERROR, "cache lookup failed for pgxc_class %u", pcrelid); /* Build array of Oids to be inserted */ nodes_array = buildoidvector(nodes, numnodes); /* Initialize fields */ MemSet(new_record, 0, sizeof(new_record)); MemSet(new_record_nulls, false, sizeof(new_record_nulls)); MemSet(new_record_repl, false, sizeof(new_record_repl)); /* Fields are updated depending on operation type */ switch (type) { case PGXC_CLASS_ALTER_DISTRIBUTION: new_record_repl[Anum_pgxc_class_pclocatortype - 1] = true; new_record_repl[Anum_pgxc_class_pcattnum - 1] = true; new_record_repl[Anum_pgxc_class_pchashalgorithm - 1] = true; new_record_repl[Anum_pgxc_class_pchashbuckets - 1] = true; break; case PGXC_CLASS_ALTER_NODES: new_record_repl[Anum_pgxc_class_nodes - 1] = true; break; case PGXC_CLASS_ALTER_ALL: default: new_record_repl[Anum_pgxc_class_pcrelid - 1] = true; new_record_repl[Anum_pgxc_class_pclocatortype - 1] = true; new_record_repl[Anum_pgxc_class_pcattnum - 1] = true; new_record_repl[Anum_pgxc_class_pchashalgorithm - 1] = true; new_record_repl[Anum_pgxc_class_pchashbuckets - 1] = true; new_record_repl[Anum_pgxc_class_nodes - 1] = true; } /* Set up new fields */ /* Relation Oid */ if (new_record_repl[Anum_pgxc_class_pcrelid - 1]) new_record[Anum_pgxc_class_pcrelid - 1] = ObjectIdGetDatum(pcrelid); /* Locator type */ if (new_record_repl[Anum_pgxc_class_pclocatortype - 1]) new_record[Anum_pgxc_class_pclocatortype - 1] = CharGetDatum(pclocatortype); /* Attribute number of distribution column */ if (new_record_repl[Anum_pgxc_class_pcattnum - 1]) new_record[Anum_pgxc_class_pcattnum - 1] = UInt16GetDatum(pcattnum); /* Hash algorithm type */ if (new_record_repl[Anum_pgxc_class_pchashalgorithm - 1]) new_record[Anum_pgxc_class_pchashalgorithm - 1] = UInt16GetDatum(pchashalgorithm); /* Hash buckets */ if (new_record_repl[Anum_pgxc_class_pchashbuckets - 1]) new_record[Anum_pgxc_class_pchashbuckets - 1] = UInt16GetDatum(pchashbuckets); /* Node information */ if (new_record_repl[Anum_pgxc_class_nodes - 1]) new_record[Anum_pgxc_class_nodes - 1] = PointerGetDatum(nodes_array); /* Update relation */ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl); simple_heap_update(rel, &oldtup->t_self, newtup); CatalogUpdateIndexes(rel, newtup); heap_close(rel, RowExclusiveLock); }
/* * AlterSequence * * Modify the definition of a sequence relation */ ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt) { Oid relid; SeqTable elm; Relation seqrel; Buffer buf; HeapTupleData seqdatatuple; Form_pg_sequence seqform; Form_pg_sequence_data seqdata; FormData_pg_sequence_data newseqdata; List *owned_by; ObjectAddress address; Relation rel; HeapTuple tuple; /* Open and lock sequence. */ relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok); if (relid == InvalidOid) { ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", stmt->sequence->relname))); return InvalidObjectAddress; } init_sequence(relid, &elm, &seqrel); /* allow ALTER to sequence owner only */ if (!pg_class_ownercheck(relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, stmt->sequence->relname); /* lock page' buffer and read tuple into new sequence structure */ seqdata = read_seq_tuple(seqrel, &buf, &seqdatatuple); /* Copy old values of options into workspace */ memcpy(&newseqdata, seqdata, sizeof(FormData_pg_sequence_data)); rel = heap_open(SequenceRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(SEQRELID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for sequence %u", relid); seqform = (Form_pg_sequence) GETSTRUCT(tuple); /* Check and set new values */ init_params(pstate, stmt->options, false, seqform, &newseqdata, &owned_by); /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ elm->cached = elm->last; /* check the comment above nextval_internal()'s equivalent call. */ if (RelationNeedsWAL(seqrel)) GetTopTransactionId(); /* Now okay to update the on-disk tuple */ START_CRIT_SECTION(); memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data)); MarkBufferDirty(buf); /* XLOG stuff */ if (RelationNeedsWAL(seqrel)) { xl_seq_rec xlrec; XLogRecPtr recptr; Page page = BufferGetPage(buf); XLogBeginInsert(); XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT); xlrec.node = seqrel->rd_node; XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len); recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG); PageSetLSN(page, recptr); } END_CRIT_SECTION(); UnlockReleaseBuffer(buf); /* process OWNED BY if given */ if (owned_by) process_owned_by(seqrel, owned_by); InvokeObjectPostAlterHook(RelationRelationId, relid, 0); ObjectAddressSet(address, RelationRelationId, relid); relation_close(seqrel, NoLock); simple_heap_update(rel, &tuple->t_self, tuple); CatalogUpdateIndexes(rel, tuple); heap_close(rel, RowExclusiveLock); return address; }
/* * create_toast_table --- internal workhorse * * rel is already opened and locked * toastOid and toastIndexOid are normally InvalidOid, but during * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, LOCKMODE lockmode, bool check) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; TupleDesc tupdesc; bool shared_relation; bool mapped_relation; Relation toast_rel; Relation class_rel; Oid toast_relid; Oid toast_typid = InvalidOid; Oid namespaceid; char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; Oid collationObjectId[2]; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, toastobject; /* * Toast table is shared if and only if its parent is. * * We cannot allow toasting a shared relation after initdb (because * there's no way to mark it toasted in other databases' pg_class). */ shared_relation = rel->rd_rel->relisshared; if (shared_relation && !IsBootstrapProcessingMode()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("shared tables cannot be toasted after initdb"))); /* It's mapped if and only if its parent is, too */ mapped_relation = RelationIsMapped(rel); /* * Is it already toasted? */ if (rel->rd_rel->reltoastrelid != InvalidOid) return false; if (!IsBinaryUpgrade) { if (!needs_toast_table(rel)) return false; } else { /* * Check to see whether the table needs a TOAST table. * * If an update-in-place TOAST relfilenode is specified, force TOAST file * creation even if it seems not to need one. This handles the case * where the old cluster needed a TOAST table but the new cluster * would not normally create one. */ /* * If a TOAST oid is not specified, skip TOAST creation as we will do * it later so we don't create a TOAST table whose OID later conflicts * with a user-supplied OID. This handles cases where the old cluster * didn't need a TOAST table, but the new cluster does. */ if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid)) return false; /* * If a special TOAST value has been passed in, it means we are in * cleanup mode --- we are creating needed TOAST tables after all user * tables with specified OIDs have been created. We let the system * assign a TOAST oid for us. The tables are empty so the missing * TOAST tables were not a problem. */ if (binary_upgrade_next_toast_pg_class_oid == OPTIONALLY_CREATE_TOAST_OID) { /* clear as it is not to be used; it is just a flag */ binary_upgrade_next_toast_pg_class_oid = InvalidOid; if (!needs_toast_table(rel)) return false; } /* both should be set, or not set */ Assert(OidIsValid(binary_upgrade_next_toast_pg_class_oid) == OidIsValid(binary_upgrade_next_toast_pg_type_oid)); } /* * If requested check lockmode is sufficient. This is a cross check in * case of errors or conflicting decisions in earlier code. */ if (check && lockmode != AccessExclusiveLock) elog(ERROR, "AccessExclusiveLock required to add toast table."); /* * Create the toast table and its index */ snprintf(toast_relname, sizeof(toast_relname), "pg_toast_%u", relOid); snprintf(toast_idxname, sizeof(toast_idxname), "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "chunk_data", BYTEAOID, -1, 0); /* * Ensure that the toast table doesn't itself get toasted, or we'll be * toast :-(. This is essential for chunk_data because type bytea is * toastable; hit the other two just to be sure. */ tupdesc->attrs[0]->attstorage = 'p'; tupdesc->attrs[1]->attstorage = 'p'; tupdesc->attrs[2]->attstorage = 'p'; /* * Toast tables for regular relations go in pg_toast; those for temp * relations go into the per-backend temp-toast-table namespace. */ if (isTempOrToastNamespace(rel->rd_rel->relnamespace)) namespaceid = GetTempToastNamespace(); else namespaceid = PG_TOAST_NAMESPACE; /* Use binary-upgrade override for pg_type.oid, if supplied. */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid)) { toast_typid = binary_upgrade_next_toast_pg_type_oid; binary_upgrade_next_toast_pg_type_oid = InvalidOid; } toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, toastOid, toast_typid, InvalidOid, rel->rd_rel->relowner, tupdesc, NIL, RELKIND_TOASTVALUE, rel->rd_rel->relpersistence, shared_relation, mapped_relation, true, 0, ONCOMMIT_NOOP, reloptions, false, true, true); Assert(toast_relid != InvalidOid); /* make the toast relation visible, else heap_open will fail */ CommandCounterIncrement(); /* ShareLock is not really needed here, but take it anyway */ toast_rel = heap_open(toast_relid, ShareLock); /* * Create unique index on chunk_id, chunk_seq. * * NOTE: the normal TOAST access routines could actually function with a * single-column index on chunk_id only. However, the slice access * routines use both columns for faster access to an individual chunk. In * addition, we want it to be unique as a check against the possibility of * duplicate TOAST chunk OIDs. The index might also be a little more * efficient this way, since btree isn't all that happy with large numbers * of equal keys. */ indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[1] = 2; indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NIL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_Unique = true; indexInfo->ii_ReadyForInserts = true; indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; collationObjectId[0] = InvalidOid; collationObjectId[1] = InvalidOid; classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; coloptions[0] = 0; coloptions[1] = 0; index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid, indexInfo, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, collationObjectId, classObjectId, coloptions, (Datum) 0, true, false, false, false, true, false, false, true); heap_close(toast_rel, NoLock); /* * Store the toast table's OID in the parent relation's pg_class row */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", relOid); ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid; if (!IsBootstrapProcessingMode()) { /* normal case, use a transactional update */ simple_heap_update(class_rel, &reltup->t_self, reltup); /* Keep catalog indexes current */ CatalogUpdateIndexes(class_rel, reltup); } else { /* While bootstrapping, we cannot UPDATE, so overwrite in-place */ heap_inplace_update(class_rel, reltup); } heap_freetuple(reltup); heap_close(class_rel, RowExclusiveLock); /* * Register dependency from the toast table to the master, so that the * toast table will be deleted if the master is. Skip this in bootstrap * mode. */ if (!IsBootstrapProcessingMode()) { baseobject.classId = RelationRelationId; baseobject.objectId = relOid; baseobject.objectSubId = 0; toastobject.classId = RelationRelationId; toastobject.objectId = toast_relid; toastobject.objectSubId = 0; recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); } /* * Make changes visible */ CommandCounterIncrement(); return true; }
/* * RenameTypeInternal * This renames a type, as well as any associated array type. * * Caller must have already checked privileges. * * Currently this is used for renaming table rowtypes and for * ALTER TYPE RENAME TO command. */ void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) { Relation pg_type_desc; HeapTuple tuple; Form_pg_type typ; Oid arrayOid; Oid oldTypeOid; pg_type_desc = table_open(TypeRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for type %u", typeOid); typ = (Form_pg_type) GETSTRUCT(tuple); /* We are not supposed to be changing schemas here */ Assert(typeNamespace == typ->typnamespace); arrayOid = typ->typarray; /* Check for a conflicting type name. */ oldTypeOid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid, CStringGetDatum(newTypeName), ObjectIdGetDatum(typeNamespace)); /* * If there is one, see if it's an autogenerated array type, and if so * rename it out of the way. (But we must skip that for a shell type * because moveArrayTypeName will do the wrong thing in that case.) * Otherwise, we can at least give a more friendly error than unique-index * violation. */ if (OidIsValid(oldTypeOid)) { if (get_typisdefined(oldTypeOid) && moveArrayTypeName(oldTypeOid, newTypeName, typeNamespace)) /* successfully dodged the problem */ ; else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", newTypeName))); } /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(typ->typname), newTypeName); CatalogTupleUpdate(pg_type_desc, &tuple->t_self, tuple); InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); heap_freetuple(tuple); table_close(pg_type_desc, RowExclusiveLock); /* * If the type has an array type, recurse to handle that. But we don't * need to do anything more if we already renamed that array type above * (which would happen when, eg, renaming "foo" to "_foo"). */ if (OidIsValid(arrayOid) && arrayOid != oldTypeOid) { char *arrname = makeArrayTypeName(newTypeName, typeNamespace); RenameTypeInternal(arrayOid, arrname, typeNamespace); pfree(arrname); } }