tablecmds.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,338 行 · 第 1/5 页
C
2,338 行
{ ScanKeyEntryInitialize(&skey[0], 0x0, Anum_pg_trigger_tgrelid, F_OIDEQ, ObjectIdGetDatum(relid)); trigscan = systable_beginscan(tgrel, TriggerRelidNameIndex, 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_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 *) 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] = ' '; } values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs); replaces[Anum_pg_trigger_tgargs - 1] = 'r'; tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces); /* * Update pg_trigger and its indexes */ simple_heap_update(tgrel, &tuple->t_self, tuple); CatalogUpdateIndexes(tgrel, tuple); /* * Invalidate trigger's relation's relcache entry so that other * backends (and this one too!) are sent SI message to make them * rebuild relcache entries. (Ideally this should happen * automatically...) * * We can skip this for triggers on relid itself, since that * relcache flush will happen anyway due to the table or column * rename. We just need to catch the far ends of RI relationships. */ pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); if (pg_trigger->tgrelid != relid) CacheInvalidateRelcache(pg_trigger->tgrelid); /* free up our scratch memory */ pfree(newtgargs); heap_freetuple(tuple); } systable_endscan(trigscan); heap_close(tgrel, RowExclusiveLock); /* * Increment cmd counter to make updates visible; this is needed in * case the same tuple has to be updated again by next pass (can * happen in case of a self-referential FK relationship). */ CommandCounterIncrement();}/* ---------------- * AlterTableAddColumn * (formerly known as PerformAddAttribute) * * adds an additional attribute to a relation * ---------------- */voidAlterTableAddColumn(Oid myrelid, bool recurse, ColumnDef *colDef){ Relation rel, pgclass, attrdesc; HeapTuple reltup; HeapTuple newreltup; HeapTuple attributeTuple; Form_pg_attribute attribute; FormData_pg_attribute attributeD; int i; int minattnum, maxatts; HeapTuple typeTuple; Form_pg_type tform; int attndims; ObjectAddress myself, referenced; /* * Grab an exclusive lock on the target table, which we will NOT * release until end of transaction. */ rel = heap_open(myrelid, AccessExclusiveLock); if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); /* * permissions checking. this would normally be done in utility.c, * but this particular routine is recursive. * * normally, only the owner of a class can change its schema. */ if (!pg_class_ownercheck(myrelid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); /* * Recurse to add the column to child classes, if requested. * * any permissions or problems with duplicate attributes will cause the * whole transaction to abort, which is what we want -- all or * nothing. */ if (recurse) { List *child, *children; ColumnDef *colDefChild = copyObject(colDef); /* Child should see column as singly inherited */ colDefChild->inhcount = 1; colDefChild->is_local = false; /* We only want direct inheritors */ children = find_inheritance_children(myrelid); foreach(child, children) { Oid childrelid = lfirsto(child); HeapTuple tuple; Form_pg_attribute childatt; Relation childrel; if (childrelid == myrelid) continue; childrel = heap_open(childrelid, AccessExclusiveLock); /* Does child already have a column by this name? */ attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock); tuple = SearchSysCacheCopyAttName(childrelid, colDef->colname); if (!HeapTupleIsValid(tuple)) { /* No, recurse to add it normally */ heap_close(attrdesc, RowExclusiveLock); heap_close(childrel, NoLock); AlterTableAddColumn(childrelid, true, colDefChild); continue; } childatt = (Form_pg_attribute) GETSTRUCT(tuple); /* Okay if child matches by type */ if (typenameTypeId(colDef->typename) != childatt->atttypid || colDef->typename->typmod != childatt->atttypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("child table \"%s\" has different type for column \"%s\"", get_rel_name(childrelid), colDef->colname))); /* * XXX if we supported NOT NULL or defaults, would need to do * more work here to verify child matches */ ereport(NOTICE, (errmsg("merging definition of column \"%s\" for child \"%s\"", colDef->colname, get_rel_name(childrelid)))); /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); CatalogUpdateIndexes(attrdesc, tuple); /* * Propagate any new CHECK constraints into the child table * and its descendants */ if (colDef->constraints != NIL) { CommandCounterIncrement(); AlterTableAddConstraint(childrelid, true, colDef->constraints); } heap_freetuple(tuple); heap_close(attrdesc, RowExclusiveLock); heap_close(childrel, NoLock); } } else { /* * If we are told not to recurse, there had better not be any * child tables; else the addition would put them out of step. */ if (find_inheritance_children(myrelid) != NIL) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column must be added to child tables too"))); } /* * OK, get on with it... * * Implementation restrictions: because we don't touch the table rows, * the new column values will initially appear to be NULLs. (This * happens because the heap tuple access routines always check for * attnum > # of attributes in tuple, and return NULL if so.) * Therefore we can't support a DEFAULT value in SQL92-compliant * fashion, and we also can't allow a NOT NULL constraint. * * We do allow CHECK constraints, even though these theoretically could * fail for NULL rows (eg, CHECK (newcol IS NOT NULL)). */ if (colDef->raw_default || colDef->cooked_default) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("adding columns with defaults is not implemented"), errhint("Add the column, then use ALTER TABLE SET DEFAULT."))); if (colDef->is_not_null) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("adding NOT NULL columns is not implemented"), errhint("Add the column, then use ALTER TABLE SET NOT NULL."))); pgclass = heap_openr(RelationRelationName, RowExclusiveLock); reltup = SearchSysCache(RELOID, ObjectIdGetDatum(myrelid), 0, 0, 0); if (!HeapTupleIsValid(reltup)) elog(ERROR, "cache lookup failed for relation %u", myrelid); /* * this test is deliberately not attisdropped-aware, since if one * tries to add a column matching a dropped column name, it's gonna * fail anyway. */ if (SearchSysCacheExists(ATTNAME, ObjectIdGetDatum(myrelid), PointerGetDatum(colDef->colname), 0, 0)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" of relation \"%s\" already exists", colDef->colname, RelationGetRelationName(rel)))); minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts; maxatts = minattnum + 1; if (maxatts > MaxHeapAttributeNumber) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("tables can have at most %d columns", MaxHeapAttributeNumber))); i = minattnum + 1; attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock); if (colDef->typename->arrayBounds) attndims = length(colDef->typename->arrayBounds); else attndims = 0; typeTuple = typenameType(colDef->typename); tform = (Form_pg_type) GETSTRUCT(typeTuple); /* make sure datatype is legal for a column */ CheckAttributeType(colDef->colname, HeapTupleGetOid(typeTuple)); attributeTuple = heap_addheader(Natts_pg_attribute, false, ATTRIBUTE_TUPLE_SIZE, (void *) &attributeD); attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple); attribute->attrelid = myrelid; namestrcpy(&(attribute->attname), colDef->colname); attribute->atttypid = HeapTupleGetOid(typeTuple); attribute->attstattarget = -1; attribute->attlen = tform->typlen; attribute->attcacheoff = -1; attribute->atttypmod = colDef->typename->typmod; attribute->attnum = i; attribute->attbyval = tform->typbyval; attribute->attndims = attndims; attribute->attisset = (bool) (tform->typtype == 'c'); attribute->attstorage = tform->typstorage; attribute->attalign = tform->typalign; attribute->attnotnull = colDef->is_not_null; attribute->atthasdef = (colDef->raw_default != NULL || colDef->cooked_default != NULL); attribute->attisdropped = false; attribute->attislocal = colDef->is_local; attribute->attinhcount = colDef->inhcount; ReleaseSysCache(typeTuple); simple_heap_insert(attrdesc, attributeTuple); /* Update indexes on pg_attribute */ CatalogUpdateIndexes(attrdesc, attributeTuple); heap_close(attrdesc, RowExclusiveLock); /* * Update number of attributes in pg_class tuple */ newreltup = heap_copytuple(reltup); ((Form_pg_class) GETSTRUCT(newreltup))->relnatts = maxatts; simple_heap_update(pgclass, &newreltup->t_self, newreltup); /* keep catalog indexes current */ CatalogUpdateIndexes(pgclass, newreltup); heap_freetuple(newreltup); ReleaseSysCache(reltup); heap_close(pgclass, RowExclusiveLock); heap_close(rel, NoLock); /* close rel but keep lock! */ /* * Add datatype dependency for the new column. */ myself.classId = RelOid_pg_class; myself.objectId = myrelid; myself.objectSubId = i; referenced.classId = RelOid_pg_type; referenced.objectId = attribute->atttypid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* * Make our catalog updates visible for subsequent steps. */ CommandCounterIncrement(); /* * Add any CHECK constraints attached to the new column. * * To do this we must re-open the rel so that its new attr list gets * loaded into the relcache. */ if (colDef->constraints != NIL) { rel = heap_open(myrelid, AccessExclusiveLock); AddRelationRawConstraints(rel, NIL, colDef->constraints); heap_close(rel, NoLock); } /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?