heap.c

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

C
2,069
字号
						(errcode(ERRCODE_DUPLICATE_OBJECT),						 errmsg("constraint \"%s\" for relation \"%s\" already exists",								ccname, RelationGetRelationName(rel))));			/* Check against other new constraints */			/* Needed because we don't do CommandCounterIncrement in loop */			foreach(listptr2, rawConstraints)			{				Constraint *cdef2 = (Constraint *) lfirst(listptr2);				if (cdef2 == cdef ||					cdef2->contype != CONSTR_CHECK ||					cdef2->raw_expr == NULL ||					cdef2->name == NULL)					continue;				if (strcmp(cdef2->name, ccname) == 0)					ereport(ERROR,							(errcode(ERRCODE_DUPLICATE_OBJECT),						 errmsg("check constraint \"%s\" already exists",								ccname)));			}		}		else		{			bool		success;			do			{				List	   *listptr2;				/*				 * Generate a name that does not conflict with				 * pre-existing constraints, nor with any auto-generated				 * names so far.				 */				ccname = GenerateConstraintName(CONSTRAINT_RELATION,												RelationGetRelid(rel),												RelationGetNamespace(rel),												&constr_name_ctr);				/*				 * Check against other new constraints, in case the user				 * has specified a name that looks like an auto-generated				 * name.				 */				success = true;				foreach(listptr2, rawConstraints)				{					Constraint *cdef2 = (Constraint *) lfirst(listptr2);					if (cdef2 == cdef ||						cdef2->contype != CONSTR_CHECK ||						cdef2->raw_expr == NULL ||						cdef2->name == NULL)						continue;					if (strcmp(cdef2->name, ccname) == 0)					{						success = false;						break;					}				}			} while (!success);		}		/*		 * Transform raw parsetree to executable expression.		 */		expr = transformExpr(pstate, cdef->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 (length(pstate->p_rtable) != 1)			ereport(ERROR,					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),					 errmsg("only table \"%s\" can be referenced in check constraint",							relname)));		/*		 * 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 function in check constraint")));		/*		 * Constraints are evaluated with execQual, which expects an		 * implicit-AND list, so convert expression to implicit-AND form.		 * (We could go so far as to convert to CNF, but that's probably		 * overkill...)		 */		expr = (Node *) make_ands_implicit((Expr *) expr);		/*		 * OK, store it.		 */		StoreRelCheck(rel, ccname, nodeToString(expr));		numchecks++;	}	/*	 * Update the count of constraints in the relation's pg_class tuple.	 * We do this even if there was no change, in order to ensure that an	 * SI update message is sent out for the pg_class tuple, which will	 * force other backends to rebuild their relcache entries for the rel.	 * (This is critical if we added defaults but not constraints.)	 */	SetRelationNumChecks(rel, numchecks);}/* * Update the count of constraints in the relation's pg_class tuple. * * Caller had better hold exclusive lock on the relation. * * An important side effect is that a SI update message will be sent out for * the pg_class tuple, which will force other backends to rebuild their * relcache entries for the rel.  Also, this backend will rebuild its * own relcache entry at the next CommandCounterIncrement. */static voidSetRelationNumChecks(Relation rel, int numchecks){	Relation	relrel;	HeapTuple	reltup;	Form_pg_class relStruct;	relrel = heap_openr(RelationRelationName, RowExclusiveLock);	reltup = SearchSysCacheCopy(RELOID,								ObjectIdGetDatum(RelationGetRelid(rel)),								0, 0, 0);	if (!HeapTupleIsValid(reltup))		elog(ERROR, "cache lookup failed for relation %u",			 RelationGetRelid(rel));	relStruct = (Form_pg_class) GETSTRUCT(reltup);	if (relStruct->relchecks != numchecks)	{		relStruct->relchecks = numchecks;		simple_heap_update(relrel, &reltup->t_self, reltup);		/* keep catalog indexes current */		CatalogUpdateIndexes(relrel, reltup);	}	else	{		/* Skip the disk update, but force relcache inval anyway */		CacheInvalidateRelcache(RelationGetRelid(rel));	}	heap_freetuple(reltup);	heap_close(relrel, RowExclusiveLock);}/* * Take a raw default and convert it to a cooked format ready for * storage. * * Parse state should be set up to recognize any vars that might appear * in the expression.  (Even though we plan to reject vars, it's more * user-friendly to give the correct error message than "unknown var".) * * If atttypid is not InvalidOid, coerce the expression to the specified * type (and typmod atttypmod).   attname is only needed in this case: * it is used in the error message, if any. */Node *cookDefault(ParseState *pstate,			Node *raw_default,			Oid atttypid,			int32 atttypmod,			char *attname){	Node	   *expr;	Assert(raw_default != NULL);	/*	 * Transform raw parsetree to executable expression.	 */	expr = transformExpr(pstate, raw_default);	/*	 * Make sure default expr does not refer to any vars.	 */	if (contain_var_clause(expr))		ereport(ERROR,				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),			  errmsg("cannot use column references in default expression")));	/*	 * It can't return a set either.	 */	if (expression_returns_set(expr))		ereport(ERROR,				(errcode(ERRCODE_DATATYPE_MISMATCH),				 errmsg("default expression must not return a set")));	/*	 * No subplans or aggregates, either...	 */	if (pstate->p_hasSubLinks)		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("cannot use subquery in default expression")));	if (pstate->p_hasAggs)		ereport(ERROR,				(errcode(ERRCODE_GROUPING_ERROR),				 errmsg("cannot use aggregate function in default expression")));	/*	 * Coerce the expression to the correct type and typmod, if given.	 * This should match the parser's processing of non-defaulted	 * expressions --- see updateTargetListEntry().	 */	if (OidIsValid(atttypid))	{		Oid			type_id = exprType(expr);		expr = coerce_to_target_type(pstate, expr, type_id,									 atttypid, atttypmod,									 COERCION_ASSIGNMENT,									 COERCE_IMPLICIT_CAST);		if (expr == NULL)			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("column \"%s\" is of type %s"							" but default expression is of type %s",							attname,							format_type_be(atttypid),							format_type_be(type_id)),			errhint("You will need to rewrite or cast the expression.")));	}	return expr;}/* * Removes all constraints on a relation that match the given name. * * It is the responsibility of the calling function to acquire a suitable * lock on the relation. * * Returns: The number of constraints removed. */intRemoveRelConstraints(Relation rel, const char *constrName,					 DropBehavior behavior){	int			ndeleted = 0;	Relation	conrel;	SysScanDesc conscan;	ScanKeyData key[1];	HeapTuple	contup;	/* Grab an appropriate lock on the pg_constraint relation */	conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);	/* Use the index to scan only constraints of the target relation */	ScanKeyEntryInitialize(&key[0], 0x0,						   Anum_pg_constraint_conrelid, F_OIDEQ,						   ObjectIdGetDatum(RelationGetRelid(rel)));	conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,								 SnapshotNow, 1, key);	/*	 * 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 = RelationGetRelid(conrel);			conobj.objectId = HeapTupleGetOid(contup);			conobj.objectSubId = 0;			performDeletion(&conobj, behavior);			ndeleted++;		}	}	/* Clean up after the scan */	systable_endscan(conscan);	heap_close(conrel, RowExclusiveLock);	return ndeleted;}/* * RemoveStatistics --- remove entries in pg_statistic for a rel or column * * If attnum is zero, remove all entries for rel; else remove only the one * for that column. */static voidRemoveStatistics(Relation rel, AttrNumber attnum){	Relation	pgstatistic;	SysScanDesc scan;	ScanKeyData key[2];	int			nkeys;	HeapTuple	tuple;	pgstatistic = heap_openr(StatisticRelationName, RowExclusiveLock);	ScanKeyEntryInitialize(&key[0], 0x0,						   Anum_pg_statistic_starelid, F_OIDEQ,						   ObjectIdGetDatum(RelationGetRelid(rel)));	if (attnum == 0)		nkeys = 1;	else	{		ScanKeyEntryInitialize(&key[1], 0x0,							   Anum_pg_statistic_staattnum, F_INT2EQ,							   Int16GetDatum(attnum));		nkeys = 2;	}	scan = systable_beginscan(pgstatistic, StatisticRelidAttnumIndex, true,							  SnapshotNow, nkeys, key);	while (HeapTupleIsValid(tuple = systable_getnext(scan)))		simple_heap_delete(pgstatistic, &tuple->t_self);	systable_endscan(scan);	heap_close(pgstatistic, RowExclusiveLock);}/* * RelationTruncateIndexes - truncate all * indexes associated with the heap relation to zero tuples. * * The routine will truncate and then reconstruct the indexes on * the relation specified by the heapId parameter. */static voidRelationTruncateIndexes(Oid heapId){	Relation	heapRelation;	List	   *indlist;	/*	 * Open the heap rel.  We need grab no lock because we assume	 * heap_truncate is holding an exclusive lock on the heap rel.	 */	heapRelation = heap_open(heapId, NoLock);	/* Ask the relcache to produce a list of the indexes of the rel */	foreach(indlist, RelationGetIndexList(heapRelation))	{		Oid			indexId = lfirsto(indlist);		Relation	currentIndex;		IndexInfo  *indexInfo;		/* Open the index relation */		currentIndex = index_open(indexId);		/* Obtain exclusive lock on it, just to be sure */		LockRelation(currentIndex, AccessExclusiveLock);		/* Fetch info needed for index_build */		indexInfo = BuildIndexInfo(currentIndex);		/*		 * Drop any buffers associated with this index. If they're dirty,		 * they're just dropped without bothering to flush to disk.		 */		DropRelationBuffers(currentIndex);		/* Now truncate the actual data and set blocks to zero */		smgrtruncate(DEFAULT_SMGR, currentIndex, 0);		currentIndex->rd_nblocks = 0;		currentIndex->rd_targblock = InvalidBlockNumber;		/* Initialize the index and rebuild */		index_build(heapRelation, currentIndex, indexInfo);		/*		 * index_build will close both the heap and index relations (but		 * not give up the locks we hold on them).	We're done with this		 * index, but we must re-open the heap rel.		 */		heapRelation = heap_open(heapId, NoLock);	}	/* Finish by closing the heap rel again */	heap_close(heapRelation, NoLock);}/* *	 heap_truncate * *	 This routine deletes all data within the specified relation. * * This is not transaction-safe!  There is another, transaction-safe * implementation in commands/cluster.c.  We now use this only for * ON COMMIT truncation of temporary tables, where it doesn't matter. */voidheap_truncate(Oid rid){	Relation	rel;	Oid			toastrelid;	/* Open relation for processing, and grab exclusive access on it. */	rel = heap_open(rid, AccessExclusiveLock);	/* Don't allow truncate on tables that are referenced by foreign keys */	heap_truncate_check_FKs(rel);	/*	 * Release any buffers associated with this relation.  If they're	 * dirty, they're just dropped without bothering to flush to disk.	 */	DropRelationBuffers(rel);	/* Now truncate the actual data and set blocks to zero */	smgrtruncate(DEFAULT_SMGR, rel, 0);	rel->rd_nblocks = 0;	rel->rd_targblock = InvalidBlockNumber;	/* If this relation has indexes, truncate the indexes too */	RelationTruncateIndexes(rid);	/* If it has a toast table, recursively truncate that too */	toastrelid = rel->rd_rel->reltoastrelid;	if (OidIsValid(toastrelid))		heap_truncate(toastrelid);	/*	 * Close the relation, but keep exclusive lock on it until commit.	 */	heap_close(rel, NoLock);}/* * heap_truncate_check_FKs *		Check for foreign keys referencing a relation that's to be truncated * * We disallow such FKs (except self-referential ones) since the whole point * of TRUNCATE is to not scan the individual rows to be thrown away. * * This is split out so it can be shared by both implementations of truncate. * Caller should already hold a suitable lock on the relation. */voidheap_truncate_check_FKs(Relation rel){	Oid			relid = RelationGetRelid(rel);	ScanKeyData key;	Relation	fkeyRel;	SysScanDesc fkeyScan;	HeapTuple	tuple;	/*	 * Fast path: if the relation has no triggers, it surely has no FKs	 * either.	 */	if (rel->rd_rel->reltriggers == 0)		return;	/*	 * Otherwise, must scan pg_constraint.  Right now, this is a seqscan	 * because there is no available index on confrelid.	 */	fkeyRel = heap_openr(ConstraintRelationName, AccessShareLock);	ScanKeyEntryInitialize(&key, 0,						   Anum_pg_constraint_confrelid,						   F_OIDEQ,						   ObjectIdGetDatum(relid));	fkeyScan = systable_beginscan(fkeyRel, NULL, false,								  SnapshotNow, 1, &key);	while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan)))	{		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);		if (con->contype == CONSTRAINT_FOREIGN && con->conrelid != relid)			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("cannot truncate a table referenced in a foreign key constraint"),					 errdetail("Table \"%s\" references \"%s\" via foreign key constraint \"%s\".",							   get_rel_name(con->conrelid),							   RelationGetRelationName(rel),							   NameStr(con->conname))));	}	systable_endscan(fkeyScan);	heap_close(fkeyRel, AccessShareLock);}

⌨️ 快捷键说明

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