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 + -
显示快捷键?