⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 typecmds.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
	/*	 * Scan over the result set, removing any matching entries.	 */	while ((contup = systable_getnext(conscan)) != NULL)	{		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);		if (strcmp(NameStr(con->conname), constrName) == 0)		{			ObjectAddress conobj;			conobj.classId = ConstraintRelationId;			conobj.objectId = HeapTupleGetOid(contup);			conobj.objectSubId = 0;			performDeletion(&conobj, behavior);		}	}	/* Clean up after the scan */	systable_endscan(conscan);	heap_close(conrel, RowExclusiveLock);	heap_close(rel, NoLock);}/* * AlterDomainAddConstraint * * Implements the ALTER DOMAIN .. ADD CONSTRAINT statement. */voidAlterDomainAddConstraint(List *names, Node *newConstraint){	TypeName   *typename;	Oid			domainoid;	Relation	typrel;	HeapTuple	tup;	Form_pg_type typTup;	List	   *rels;	ListCell   *rt;	EState	   *estate;	ExprContext *econtext;	char	   *ccbin;	Expr	   *expr;	ExprState  *exprstate;	Constraint *constr;	/* Make a TypeName so we can use standard type lookup machinery */	typename = makeNode(TypeName);	typename->names = names;	typename->typmod = -1;	typename->arrayBounds = NIL;	/* Lock the type table */	typrel = heap_open(TypeRelationId, RowExclusiveLock);	/* Use LookupTypeName here so that shell types can be found (why?). */	domainoid = LookupTypeName(typename);	if (!OidIsValid(domainoid))		ereport(ERROR,				(errcode(ERRCODE_UNDEFINED_OBJECT),				 errmsg("type \"%s\" does not exist",						TypeNameToString(typename))));	tup = SearchSysCacheCopy(TYPEOID,							 ObjectIdGetDatum(domainoid),							 0, 0, 0);	if (!HeapTupleIsValid(tup))		elog(ERROR, "cache lookup failed for type %u", domainoid);	typTup = (Form_pg_type) GETSTRUCT(tup);	/* Doesn't return if user isn't allowed to alter the domain */	domainOwnerCheck(tup, typename);	/* Check for unsupported constraint types */	if (IsA(newConstraint, FkConstraint))		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("foreign key constraints not possible for domains")));	/* otherwise it should be a plain Constraint */	if (!IsA(newConstraint, Constraint))		elog(ERROR, "unrecognized node type: %d",			 (int) nodeTag(newConstraint));	constr = (Constraint *) newConstraint;	switch (constr->contype)	{		case CONSTR_CHECK:			/* processed below */			break;		case CONSTR_UNIQUE:			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("unique constraints not possible for domains")));			break;		case CONSTR_PRIMARY:			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),				errmsg("primary key constraints not possible for domains")));			break;		case CONSTR_ATTR_DEFERRABLE:		case CONSTR_ATTR_NOT_DEFERRABLE:		case CONSTR_ATTR_DEFERRED:		case CONSTR_ATTR_IMMEDIATE:			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("specifying constraint deferrability not supported for domains")));			break;		default:			elog(ERROR, "unrecognized constraint subtype: %d",				 (int) constr->contype);			break;	}	/*	 * Since all other constraint types throw errors, this must be a check	 * constraint.	First, process the constraint expression and add an entry	 * to pg_constraint.	 */	ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,								typTup->typbasetype, typTup->typtypmod,								constr, NameStr(typTup->typname));	/*	 * Test all values stored in the attributes based on the domain the	 * constraint is being added to.	 */	expr = (Expr *) stringToNode(ccbin);	/* Need an EState to run ExecEvalExpr */	estate = CreateExecutorState();	econtext = GetPerTupleExprContext(estate);	/* build execution state for expr */	exprstate = ExecPrepareExpr(expr, estate);	/* Fetch relation list with attributes based on this domain */	/* ShareLock is sufficient to prevent concurrent data changes */	rels = get_rels_with_domain(domainoid, ShareLock);	foreach(rt, rels)	{		RelToCheck *rtc = (RelToCheck *) lfirst(rt);		Relation	testrel = rtc->rel;		TupleDesc	tupdesc = RelationGetDescr(testrel);		HeapScanDesc scan;		HeapTuple	tuple;		/* Scan all tuples in this relation */		scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);		while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)		{			int			i;			/* Test attributes that are of the domain */			for (i = 0; i < rtc->natts; i++)			{				int			attnum = rtc->atts[i];				Datum		d;				bool		isNull;				Datum		conResult;				d = heap_getattr(tuple, attnum, tupdesc, &isNull);				econtext->domainValue_datum = d;				econtext->domainValue_isNull = isNull;				conResult = ExecEvalExprSwitchContext(exprstate,													  econtext,													  &isNull, NULL);				if (!isNull && !DatumGetBool(conResult))					ereport(ERROR,							(errcode(ERRCODE_CHECK_VIOLATION),							 errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",								NameStr(tupdesc->attrs[attnum - 1]->attname),									RelationGetRelationName(testrel))));			}			ResetExprContext(econtext);		}		heap_endscan(scan);		/* Hold relation lock till commit (XXX bad for concurrency) */		heap_close(testrel, NoLock);	}	FreeExecutorState(estate);	/* Clean up */	heap_close(typrel, RowExclusiveLock);}/* * get_rels_with_domain * * Fetch all relations / attributes which are using the domain * * The result is a list of RelToCheck structs, one for each distinct * relation, each containing one or more attribute numbers that are of * the domain type.  We have opened each rel and acquired the specified lock * type on it. * * XXX this is completely broken because there is no way to lock the domain * to prevent columns from being added or dropped while our command runs. * We can partially protect against column drops by locking relations as we * come across them, but there is still a race condition (the window between * seeing a pg_depend entry and acquiring lock on the relation it references). * Also, holding locks on all these relations simultaneously creates a non- * trivial risk of deadlock.  We can minimize but not eliminate the deadlock * risk by using the weakest suitable lock (ShareLock for most callers). * * XXX to support domains over domains, we'd need to make this smarter, * or make its callers smarter, so that we could find columns of derived * domains.  Arrays of domains would be a problem too. * * Generally used for retrieving a list of tests when adding * new constraints to a domain. */static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode){	List	   *result = NIL;	Relation	depRel;	ScanKeyData key[2];	SysScanDesc depScan;	HeapTuple	depTup;	/*	 * We scan pg_depend to find those things that depend on the domain. (We	 * assume we can ignore refobjsubid for a domain.)	 */	depRel = heap_open(DependRelationId, AccessShareLock);	ScanKeyInit(&key[0],				Anum_pg_depend_refclassid,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(TypeRelationId));	ScanKeyInit(&key[1],				Anum_pg_depend_refobjid,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(domainOid));	depScan = systable_beginscan(depRel, DependReferenceIndexId, true,								 SnapshotNow, 2, key);	while (HeapTupleIsValid(depTup = systable_getnext(depScan)))	{		Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);		RelToCheck *rtc = NULL;		ListCell   *rellist;		Form_pg_attribute pg_att;		int			ptr;		/* Ignore dependees that aren't user columns of relations */		/* (we assume system columns are never of domain types) */		if (pg_depend->classid != RelationRelationId ||			pg_depend->objsubid <= 0)			continue;		/* See if we already have an entry for this relation */		foreach(rellist, result)		{			RelToCheck *rt = (RelToCheck *) lfirst(rellist);			if (RelationGetRelid(rt->rel) == pg_depend->objid)			{				rtc = rt;				break;			}		}		if (rtc == NULL)		{			/* First attribute found for this relation */			Relation	rel;			/* Acquire requested lock on relation */			rel = relation_open(pg_depend->objid, lockmode);			/* It could be a view or composite type; if so ignore it */			if (rel->rd_rel->relkind != RELKIND_RELATION)			{				relation_close(rel, lockmode);				continue;			}			/* Build the RelToCheck entry with enough space for all atts */			rtc = (RelToCheck *) palloc(sizeof(RelToCheck));			rtc->rel = rel;			rtc->natts = 0;			rtc->atts = (int *) palloc(sizeof(int) * RelationGetNumberOfAttributes(rel));			result = lcons(rtc, result);		}		/*		 * Confirm column has not been dropped, and is of the expected type.		 * This defends against an ALTER DROP COLUMN occuring just before we		 * acquired lock ... but if the whole table were dropped, we'd still		 * have a problem.		 */		if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))			continue;		pg_att = rtc->rel->rd_att->attrs[pg_depend->objsubid - 1];		if (pg_att->attisdropped || pg_att->atttypid != domainOid)			continue;		/*		 * Okay, add column to result.	We store the columns in column-number		 * order; this is just a hack to improve predictability of regression		 * test output ...		 */		Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));		ptr = rtc->natts++;		while (ptr > 0 && rtc->atts[ptr - 1] > pg_depend->objsubid)		{			rtc->atts[ptr] = rtc->atts[ptr - 1];			ptr--;		}		rtc->atts[ptr] = pg_depend->objsubid;	}	systable_endscan(depScan);	relation_close(depRel, AccessShareLock);	return result;}/* * domainOwnerCheck * * Throw an error if the current user doesn't have permission to modify * the domain in an ALTER DOMAIN statement, or if the type isn't actually * a domain. */static voiddomainOwnerCheck(HeapTuple tup, TypeName *typename){	Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);	/* Check that this is actually a domain */	if (typTup->typtype != 'd')		ereport(ERROR,				(errcode(ERRCODE_WRONG_OBJECT_TYPE),				 errmsg("\"%s\" is not a domain",						TypeNameToString(typename))));	/* Permission check: must own type */	if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,					   TypeNameToString(typename));}/* * domainAddConstraint - code shared between CREATE and ALTER DOMAIN */static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,					int typMod, Constraint *constr,					char *domainName){	Node	   *expr;	char	   *ccsrc;	char	   *ccbin;	ParseState *pstate;	CoerceToDomainValue *domVal;	/*	 * Assign or validate constraint name	 */	if (constr->name)	{		if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,								 domainOid,								 domainNamespace,								 constr->name))			ereport(ERROR,					(errcode(ERRCODE_DUPLICATE_OBJECT),				 errmsg("constraint \"%s\" for domain \"%s\" already exists",						constr->name, domainName)));	}	else		constr->name = ChooseConstraintName(domainName,											NULL,											"check",											domainNamespace,											NIL);	/*	 * Convert the A_EXPR in raw_expr into an EXPR	 */	pstate = make_parsestate(NULL);	/*	 * Set up a CoerceToDomainValue to represent the occurrence of VALUE in	 * the expression.	Note that it will appear to have the type of the base	 * type, not the domain.  This seems correct since within the check	 * expression, we should not assume the input value can be considered a	 * member of the domain.	 */	domVal = makeNode(CoerceToDomainValue);	domVal->typeId = baseTypeOid;	domVal->typeMod = typMod;	pstate->p_value_substitute = (Node *) domVal;	expr = transformExpr(pstate, constr->raw_expr);	/*	 * Make sure it yields a boolean result.	 */	expr = coerce_to_boolean(pstate, expr, "CHECK");	/*	 * Make sure no outside relations are referred to.	 */	if (list_length(pstate->p_rtable) != 0)		ereport(ERROR,				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),		  errmsg("cannot use table references in domain check constraint")));	/*	 * Domains don't allow var clauses (this should be redundant with the	 * above check, but make it anyway)	 */	if (contain_var_clause(expr))		ereport(ERROR,				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),		  errmsg("cannot use table references in domain check constraint")));	/*	 * No subplans or aggregates, either...	 */	if (pstate->p_hasSubLinks)		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("cannot use subquery in check constraint")));	if (pstate->p_hasAggs)		ereport(ERROR,				(errcode(ERRCODE_GROUPING_ERROR),				 errmsg("cannot use aggregate in check constraint")));	/*	 * Convert to string form for storage.	 */	ccbin = nodeToString(expr);	/*	 * Deparse it to produce text for consrc.	 *

⌨️ 快捷键说明

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