tablecmds.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,338 行 · 第 1/5 页

C
2,338
字号
/*------------------------------------------------------------------------- * * tablecmds.c *	  Commands for creating and altering table structures and settings * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.91.2.1 2004/07/17 17:28:47 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/tuptoaster.h"#include "catalog/catalog.h"#include "catalog/catname.h"#include "catalog/dependency.h"#include "catalog/heap.h"#include "catalog/index.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_constraint.h"#include "catalog/pg_inherits.h"#include "catalog/pg_namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_trigger.h"#include "catalog/pg_type.h"#include "commands/cluster.h"#include "commands/tablecmds.h"#include "commands/trigger.h"#include "executor/executor.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/plancat.h"#include "optimizer/prep.h"#include "parser/gramparse.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parse_relation.h"#include "parser/parse_type.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/relcache.h"#include "utils/syscache.h"/* * ON COMMIT action list */typedef struct OnCommitItem{	Oid			relid;			/* relid of relation */	OnCommitAction oncommit;	/* what to do at end of xact */	/*	 * If this entry was created during this xact, it should be deleted at	 * xact abort.	Conversely, if this entry was deleted during this	 * xact, it should be removed at xact commit.  We leave deleted	 * entries in the list until commit so that we can roll back if	 * needed.	 */	bool		created_in_cur_xact;	bool		deleted_in_cur_xact;} OnCommitItem;static List *on_commits = NIL;static List *MergeAttributes(List *schema, List *supers, bool istemp,				List **supOids, List **supconstr, bool *supHasOids);static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);static void StoreCatalogInheritance(Oid relationId, List *supers);static int	findAttrByName(const char *attributeName, List *schema);static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);static bool needs_toast_table(Relation rel);static void AlterTableAddCheckConstraint(Relation rel, Constraint *constr);static void AlterTableAddForeignKeyConstraint(Relation rel,								  FkConstraint *fkconstraint);static int transformColumnNameList(Oid relId, List *colList,						int16 *attnums, Oid *atttypids);static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,						   List **attnamelist,						   int16 *attnums, Oid *atttypids);static Oid transformFkeyCheckAttrs(Relation pkrel,						int numattrs, int16 *attnums);static void validateForeignKeyConstraint(FkConstraint *fkconstraint,							 Relation rel, Relation pkrel);static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,						 Oid constrOid);static char *fkMatchTypeToString(char match_type);/* Used by attribute and relation renaming routines: */#define RI_TRIGGER_PK	1		/* is a trigger on the PK relation */#define RI_TRIGGER_FK	2		/* is a trigger on the FK relation */#define RI_TRIGGER_NONE 0		/* is not an RI trigger function */static int	ri_trigger_type(Oid tgfoid);static void update_ri_trigger_args(Oid relid,					   const char *oldname,					   const char *newname,					   bool fk_scan,					   bool update_relname);/* ---------------------------------------------------------------- *		DefineRelation *				Creates a new relation. * * If successful, returns the OID of the new relation. * ---------------------------------------------------------------- */OidDefineRelation(CreateStmt *stmt, char relkind){	char		relname[NAMEDATALEN];	Oid			namespaceId;	List	   *schema = stmt->tableElts;	Oid			relationId;	Relation	rel;	TupleDesc	descriptor;	List	   *inheritOids;	List	   *old_constraints;	bool		parentHasOids;	List	   *rawDefaults;	List	   *listptr;	int			i;	AttrNumber	attnum;	/*	 * Truncate relname to appropriate length (probably a waste of time,	 * as parser should have done this already).	 */	StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);	/*	 * Check consistency of arguments	 */	if (stmt->oncommit != ONCOMMIT_NOOP && !stmt->relation->istemp)		ereport(ERROR,				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),				 errmsg("ON COMMIT can only be used on temporary tables")));	/*	 * Look up the namespace in which we are supposed to create the	 * relation.  Check we have permission to create there. Skip check if	 * bootstrapping, since permissions machinery may not be working yet.	 */	namespaceId = RangeVarGetCreationNamespace(stmt->relation);	if (!IsBootstrapProcessingMode())	{		AclResult	aclresult;		aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),										  ACL_CREATE);		if (aclresult != ACLCHECK_OK)			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,						   get_namespace_name(namespaceId));	}	/*	 * Look up inheritance ancestors and generate relation schema,	 * including inherited attributes.	 */	schema = MergeAttributes(schema, stmt->inhRelations,							 stmt->relation->istemp,						 &inheritOids, &old_constraints, &parentHasOids);	/*	 * Create a relation descriptor from the relation schema and create	 * the relation.  Note that in this stage only inherited (pre-cooked)	 * defaults and constraints will be included into the new relation.	 * (BuildDescForRelation takes care of the inherited defaults, but we	 * have to copy inherited constraints here.)	 */	descriptor = BuildDescForRelation(schema);	descriptor->tdhasoid = (stmt->hasoids || parentHasOids);	if (old_constraints != NIL)	{		ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *													sizeof(ConstrCheck));		int			ncheck = 0;		int			constr_name_ctr = 0;		foreach(listptr, old_constraints)		{			Constraint *cdef = (Constraint *) lfirst(listptr);			if (cdef->contype != CONSTR_CHECK)				continue;			if (cdef->name != NULL)			{				for (i = 0; i < ncheck; i++)				{					if (strcmp(check[i].ccname, cdef->name) == 0)						ereport(ERROR,								(errcode(ERRCODE_DUPLICATE_OBJECT),						 errmsg("duplicate check constraint name \"%s\"",								cdef->name)));				}				check[ncheck].ccname = cdef->name;			}			else			{				/*				 * Generate a constraint name.	NB: this should match the				 * form of names that GenerateConstraintName() may produce				 * for names added later.  We are assured that there is no				 * name conflict, because MergeAttributes() did not pass				 * back any names of this form.				 */				check[ncheck].ccname = (char *) palloc(NAMEDATALEN);				snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d",						 ++constr_name_ctr);			}			Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);			check[ncheck].ccbin = pstrdup(cdef->cooked_expr);			ncheck++;		}		if (ncheck > 0)		{			if (descriptor->constr == NULL)			{				descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));				descriptor->constr->defval = NULL;				descriptor->constr->num_defval = 0;				descriptor->constr->has_not_null = false;			}			descriptor->constr->num_check = ncheck;			descriptor->constr->check = check;		}	}	relationId = heap_create_with_catalog(relname,										  namespaceId,										  descriptor,										  relkind,										  false,										  stmt->oncommit,										  allowSystemTableMods);	StoreCatalogInheritance(relationId, inheritOids);	/*	 * We must bump the command counter to make the newly-created relation	 * tuple visible for opening.	 */	CommandCounterIncrement();	/*	 * Open the new relation and acquire exclusive lock on it.	This isn't	 * really necessary for locking out other backends (since they can't	 * see the new rel anyway until we commit), but it keeps the lock	 * manager from complaining about deadlock risks.	 */	rel = relation_open(relationId, AccessExclusiveLock);	/*	 * Now add any newly specified column default values and CHECK	 * constraints to the new relation.  These are passed to us in the	 * form of raw parsetrees; we need to transform them to executable	 * expression trees before they can be added. The most convenient way	 * to do that is to apply the parser's transformExpr routine, but	 * transformExpr doesn't work unless we have a pre-existing relation.	 * So, the transformation has to be postponed to this final step of	 * CREATE TABLE.	 *	 * Another task that's conveniently done at this step is to add	 * dependency links between columns and supporting relations (such as	 * SERIAL sequences).	 *	 * First, scan schema to find new column defaults.	 */	rawDefaults = NIL;	attnum = 0;	foreach(listptr, schema)	{		ColumnDef  *colDef = lfirst(listptr);		attnum++;		if (colDef->raw_default != NULL)		{			RawColumnDefault *rawEnt;			Assert(colDef->cooked_default == NULL);			rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));			rawEnt->attnum = attnum;			rawEnt->raw_default = colDef->raw_default;			rawDefaults = lappend(rawDefaults, rawEnt);		}		if (colDef->support != NULL)		{			/* Create dependency for supporting relation for this column */			ObjectAddress colobject,						suppobject;			colobject.classId = RelOid_pg_class;			colobject.objectId = relationId;			colobject.objectSubId = attnum;			suppobject.classId = RelOid_pg_class;			suppobject.objectId = RangeVarGetRelid(colDef->support, false);			suppobject.objectSubId = 0;			recordDependencyOn(&suppobject, &colobject, DEPENDENCY_INTERNAL);		}	}	/*	 * Parse and add the defaults/constraints, if any.	 */	if (rawDefaults || stmt->constraints)		AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);	/*	 * Clean up.  We keep lock on new relation (although it shouldn't be	 * visible to anyone else anyway, until commit).	 */	relation_close(rel, NoLock);	return relationId;}/* * RemoveRelation *		Deletes a relation. */voidRemoveRelation(const RangeVar *relation, DropBehavior behavior){	Oid			relOid;	ObjectAddress object;	relOid = RangeVarGetRelid(relation, false);	object.classId = RelOid_pg_class;	object.objectId = relOid;	object.objectSubId = 0;	performDeletion(&object, behavior);}/* * TruncateRelation *		Removes all the rows from a relation. * * Note: This routine only does safety and permissions checks; * rebuild_relation in cluster.c does the actual work. */voidTruncateRelation(const RangeVar *relation){	Relation	rel;	/* Grab exclusive lock in preparation for truncate */	rel = heap_openrv(relation, AccessExclusiveLock);	/* Only allow truncate on regular tables */	if (rel->rd_rel->relkind != RELKIND_RELATION)		ereport(ERROR,				(errcode(ERRCODE_WRONG_OBJECT_TYPE),				 errmsg("\"%s\" is not a table",						RelationGetRelationName(rel))));	/* Permissions checks */	if (!pg_class_ownercheck(RelationGetRelid(rel), 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))));	/*	 * Don't allow truncate on temp tables of other backends ... their	 * local buffer manager is not going to cope.	 */	if (isOtherTempNamespace(RelationGetNamespace(rel)))		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),			  errmsg("cannot truncate temporary tables of other sessions")));	/*	 * Don't allow truncate on tables which are referenced by foreign keys	 */	heap_truncate_check_FKs(rel);	/*	 * Do the real work using the same technique as cluster, but without	 * the data-copying portion	 */	rebuild_relation(rel, InvalidOid);	/* NB: rebuild_relation does heap_close() */	/*	 * You might think we need to truncate the rel's toast table here too,	 * but actually we don't; it will have been rebuilt in an empty state.	 */}/*---------- * MergeAttributes *		Returns new schema given initial schema and superclasses. * * Input arguments: * 'schema' is the column/attribute definition for the table. (It's a list *		of ColumnDef's.) It is destructively changed. * 'supers' is a list of names (as RangeVar nodes) of parent relations. * 'istemp' is TRUE if we are creating a temp relation. * * Output arguments: * 'supOids' receives a list of the OIDs of the parent relations. * 'supconstr' receives a list of constraints belonging to the parents, *		updated as necessary to be valid for the child. * 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE. * * Return value: * Completed schema list. * * Notes: *	  The order in which the attributes are inherited is very important. *	  Intuitively, the inherited attributes should come first. If a table *	  inherits from multiple parents, the order of those attributes are *	  according to the order of the parents specified in CREATE TABLE. * *	  Here's an example: * *		create table person (name text, age int4, location point); *		create table emp (salary int4, manager text) inherits(person); *		create table student (gpa float8) inherits (person); *		create table stud_emp (percent int4) inherits (emp, student); * *	  The order of the attributes of stud_emp is: * *							person {1:name, 2:age, 3:location} *							/	 \ *			   {6:gpa}	student   emp {4:salary, 5:manager} *							\	 / *						   stud_emp {7:percent} * *	   If the same attribute name appears multiple times, then it appears *	   in the result table in the proper location for its first appearance. * *	   Constraints (including NOT NULL constraints) for the child table *	   are the union of all relevant constraints, from both the child schema *	   and parent tables. * *	   The default value for a child column is defined as: *		(1) If the child schema specifies a default, that value is used. *		(2) If neither the child nor any parent specifies a default, then

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?