📄 tablecmds.c
字号:
if (!pg_class_ownercheck(myrelid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(targetrelation)); if (!allowSystemTableMods && IsSystemRelation(targetrelation)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(targetrelation)))); /* * if the 'recurse' flag is set then we are supposed to rename this * attribute in all classes that inherit from 'relname' (as well as in * 'relname'). * * any permissions or problems with duplicate attributes will cause the * whole transaction to abort, which is what we want -- all or nothing. */ if (recurse) { ListCell *child; List *children; /* this routine is actually in the planner */ children = find_all_inheritors(myrelid); /* * find_all_inheritors does the recursive search of the inheritance * hierarchy, so all we have to do is process all of the relids in the * list that it returns. */ foreach(child, children) { Oid childrelid = lfirst_oid(child); if (childrelid == myrelid) continue; /* note we need not recurse again */ renameatt(childrelid, oldattname, newattname, false, true); } } else { /* * If we are told not to recurse, there had better not be any child * tables; else the rename would put them out of step. */ if (!recursing && find_inheritance_children(myrelid) != NIL) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("inherited column \"%s\" must be renamed in child tables too", oldattname))); } attrelation = heap_open(AttributeRelationId, RowExclusiveLock); atttup = SearchSysCacheCopyAttName(myrelid, oldattname); if (!HeapTupleIsValid(atttup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" does not exist", oldattname))); attform = (Form_pg_attribute) GETSTRUCT(atttup); attnum = attform->attnum; if (attnum <= 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot rename system column \"%s\"", oldattname))); /* * if the attribute is inherited, forbid the renaming, unless we are * already inside a recursive rename. */ if (attform->attinhcount > 0 && !recursing) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot rename inherited column \"%s\"", oldattname))); /* should not already exist */ /* this test is deliberately not attisdropped-aware */ if (SearchSysCacheExists(ATTNAME, ObjectIdGetDatum(myrelid), PointerGetDatum(newattname), 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" of relation \"%s\" already exists", newattname, RelationGetRelationName(targetrelation)))); namestrcpy(&(attform->attname), newattname); simple_heap_update(attrelation, &atttup->t_self, atttup); /* keep system catalog indexes current */ CatalogUpdateIndexes(attrelation, atttup); heap_freetuple(atttup); /* * Update column names of indexes that refer to the column being renamed. */ indexoidlist = RelationGetIndexList(targetrelation); foreach(indexoidscan, indexoidlist) { Oid indexoid = lfirst_oid(indexoidscan); HeapTuple indextup; Form_pg_index indexform; int i; /* * Scan through index columns to see if there's any simple index * entries for this attribute. We ignore expressional entries. */ indextup = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexoid), 0, 0, 0); if (!HeapTupleIsValid(indextup)) elog(ERROR, "cache lookup failed for index %u", indexoid); indexform = (Form_pg_index) GETSTRUCT(indextup); for (i = 0; i < indexform->indnatts; i++) { if (attnum != indexform->indkey.values[i]) continue; /* * Found one, rename it. */ atttup = SearchSysCacheCopy(ATTNUM, ObjectIdGetDatum(indexoid), Int16GetDatum(i + 1), 0, 0); if (!HeapTupleIsValid(atttup)) continue; /* should we raise an error? */ /* * Update the (copied) attribute tuple. */ namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname), newattname); simple_heap_update(attrelation, &atttup->t_self, atttup); /* keep system catalog indexes current */ CatalogUpdateIndexes(attrelation, atttup); heap_freetuple(atttup); } ReleaseSysCache(indextup); } list_free(indexoidlist); heap_close(attrelation, RowExclusiveLock); /* * Update att name in any RI triggers associated with the relation. */ if (targetrelation->rd_rel->reltriggers > 0) { /* update tgargs column reference where att is primary key */ update_ri_trigger_args(RelationGetRelid(targetrelation), oldattname, newattname, false, false); /* update tgargs column reference where att is foreign key */ update_ri_trigger_args(RelationGetRelid(targetrelation), oldattname, newattname, true, false); } relation_close(targetrelation, NoLock); /* close rel but keep lock */}/* * renamerel - change the name of a relation * * XXX - When renaming sequences, we don't bother to modify the * sequence name that is stored within the sequence itself * (this would cause problems with MVCC). In the future, * the sequence name should probably be removed from the * sequence, AFAIK there's no need for it to be there. */voidrenamerel(Oid myrelid, const char *newrelname){ Relation targetrelation; Relation relrelation; /* for RELATION relation */ HeapTuple reltup; Oid namespaceId; char *oldrelname; char relkind; bool relhastriggers; /* * Grab an exclusive lock on the target table or index, which we will NOT * release until end of transaction. */ targetrelation = relation_open(myrelid, AccessExclusiveLock); oldrelname = pstrdup(RelationGetRelationName(targetrelation)); namespaceId = RelationGetNamespace(targetrelation); if (!allowSystemTableMods && IsSystemRelation(targetrelation)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(targetrelation)))); relkind = targetrelation->rd_rel->relkind; relhastriggers = (targetrelation->rd_rel->reltriggers > 0); /* * Find relation's pg_class tuple, and make sure newrelname isn't in use. */ relrelation = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy(RELOID, PointerGetDatum(myrelid), 0, 0, 0); if (!HeapTupleIsValid(reltup)) /* shouldn't happen */ elog(ERROR, "cache lookup failed for relation %u", myrelid); if (get_relname_relid(newrelname, namespaceId) != InvalidOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("relation \"%s\" already exists", newrelname))); /* * Update pg_class tuple with new relname. (Scribbling on reltup is OK * because it's a copy...) */ namestrcpy(&(((Form_pg_class) GETSTRUCT(reltup))->relname), newrelname); simple_heap_update(relrelation, &reltup->t_self, reltup); /* keep the system catalog indexes current */ CatalogUpdateIndexes(relrelation, reltup); heap_freetuple(reltup); heap_close(relrelation, RowExclusiveLock); /* * Also rename the associated type, if any. */ if (relkind != RELKIND_INDEX) TypeRename(oldrelname, namespaceId, newrelname); /* * Update rel name in any RI triggers associated with the relation. */ if (relhastriggers) { /* update tgargs where relname is primary key */ update_ri_trigger_args(myrelid, oldrelname, newrelname, false, true); /* update tgargs where relname is foreign key */ update_ri_trigger_args(myrelid, oldrelname, newrelname, true, true); } /* * Close rel, but keep exclusive lock! */ relation_close(targetrelation, NoLock);}/* * Scan pg_trigger for RI triggers that are on the specified relation * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan * is true). Update RI trigger args fields matching oldname to contain * newname instead. If update_relname is true, examine the relname * fields; otherwise examine the attname fields. */static voidupdate_ri_trigger_args(Oid relid, const char *oldname, const char *newname, bool fk_scan, bool update_relname){ Relation tgrel; ScanKeyData skey[1]; SysScanDesc trigscan; HeapTuple tuple; Datum values[Natts_pg_trigger]; char nulls[Natts_pg_trigger]; char replaces[Natts_pg_trigger]; tgrel = heap_open(TriggerRelationId, RowExclusiveLock); if (fk_scan) { ScanKeyInit(&skey[0], Anum_pg_trigger_tgconstrrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); trigscan = systable_beginscan(tgrel, TriggerConstrRelidIndexId, true, SnapshotNow, 1, skey); } else { ScanKeyInit(&skey[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); trigscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 1, skey); } while ((tuple = systable_getnext(trigscan)) != NULL) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); bytea *val; bytea *newtgargs; bool isnull; int tg_type; bool examine_pk; bool changed; int tgnargs; int i; int newlen; const char *arga[RI_MAX_ARGUMENTS]; const char *argp; tg_type = RI_FKey_trigger_type(pg_trigger->tgfoid); if (tg_type == RI_TRIGGER_NONE) { /* Not an RI trigger, forget it */ continue; } /* * It is an RI trigger, so parse the tgargs bytea. * * NB: we assume the field will never be compressed or moved out of * line; so does trigger.c ... */ tgnargs = pg_trigger->tgnargs; val = (bytea *) DatumGetPointer(fastgetattr(tuple, Anum_pg_trigger_tgargs, tgrel->rd_att, &isnull)); if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO || tgnargs > RI_MAX_ARGUMENTS) { /* This probably shouldn't happen, but ignore busted triggers */ continue; } argp = (const char *) VARDATA(val); for (i = 0; i < tgnargs; i++) { arga[i] = argp; argp += strlen(argp) + 1; } /* * Figure out which item(s) to look at. If the trigger is primary-key * type and attached to my rel, I should look at the PK fields; if it * is foreign-key type and attached to my rel, I should look at the FK * fields. But the opposite rule holds when examining triggers found * by tgconstrrel search. */ examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan); changed = false; if (update_relname) { /* Change the relname if needed */ i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO; if (strcmp(arga[i], oldname) == 0) { arga[i] = newname; changed = true; } } else { /* Change attname(s) if needed */ i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX : RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX; for (; i < tgnargs; i += 2) { if (strcmp(arga[i], oldname) == 0) { arga[i] = newname; changed = true; } } } if (!changed) { /* Don't need to update this tuple */ continue; } /* * Construct modified tgargs bytea. */ newlen = VARHDRSZ; for (i = 0; i < tgnargs; i++) newlen += strlen(arga[i]) + 1; newtgargs = (bytea *) palloc(newlen); VARATT_SIZEP(newtgargs) = newlen; newlen = VARHDRSZ; for (i = 0; i < tgnargs; i++) { strcpy(((char *) newtgargs) + newlen, arga[i]); newlen += strlen(arga[i]) + 1; } /* * Build modified tuple. */ for (i = 0; i < Natts_pg_trigger; i++) { values[i] = (Datum) 0; replaces[i] = ' '; nulls[i] = ' ';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -