ri_triggers.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,476 行 · 第 1/5 页

C
2,476
字号
	if (SPI_finish() != SPI_OK_FINISH)		elog(ERROR, "SPI_finish failed");	heap_close(pk_rel, RowShareLock);	return PointerGetDatum(NULL);}/* ---------- * RI_FKey_check_ins - * *	Check foreign key existence at insert event on FK table. * ---------- */DatumRI_FKey_check_ins(PG_FUNCTION_ARGS){	return RI_FKey_check(fcinfo);}/* ---------- * RI_FKey_check_upd - * *	Check foreign key existence at update event on FK table. * ---------- */DatumRI_FKey_check_upd(PG_FUNCTION_ARGS){	return RI_FKey_check(fcinfo);}/* ---------- * ri_Check_Pk_Match * * Check for matching value of old pk row in current state for * noaction triggers. Returns false if no row was found and a fk row * could potentially be referencing this row, true otherwise. * ---------- */static boolri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,				  HeapTuple old_row,				  const RI_ConstraintInfo *riinfo){	SPIPlanPtr	qplan;	RI_QueryKey qkey;	int			i;	bool		result;	ri_BuildQueryKeyPkCheck(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);	switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))	{		case RI_KEYS_ALL_NULL:			/*			 * No check - nothing could have been referencing this row anyway.			 */			return true;		case RI_KEYS_SOME_NULL:			/*			 * This is the only case that differs between the three kinds of			 * MATCH.			 */			switch (riinfo->confmatchtype)			{				case FKCONSTR_MATCH_FULL:				case FKCONSTR_MATCH_UNSPECIFIED:					/*					 * MATCH <unspecified>/FULL  - if ANY column is null, we					 * can't be matching to this row already.					 */					return true;				case FKCONSTR_MATCH_PARTIAL:					/*					 * MATCH PARTIAL - all non-null columns must match. (not					 * implemented, can be done by modifying the query below					 * to only include non-null columns, or by writing a					 * special version here)					 */					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("MATCH PARTIAL not yet implemented")));					break;			}		case RI_KEYS_NONE_NULL:			/*			 * Have a full qualified key - continue below for all three kinds			 * of MATCH.			 */			break;	}	if (SPI_connect() != SPI_OK_CONNECT)		elog(ERROR, "SPI_connect failed");	/*	 * Fetch or prepare a saved plan for the real check	 */	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)	{		StringInfoData querybuf;		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];		char		attname[MAX_QUOTED_NAME_LEN];		char		paramname[16];		const char *querysep;		Oid			queryoids[RI_MAX_NUMKEYS];		/* ----------		 * The query string built is		 *	SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE		 * The type id's for the $ parameters are those of the		 * PK attributes themselves.		 * ----------		 */		initStringInfo(&querybuf);		quoteRelationName(pkrelname, pk_rel);		appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);		querysep = "WHERE";		for (i = 0; i < riinfo->nkeys; i++)		{			Oid			pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);			quoteOneName(attname,						 RIAttName(pk_rel, riinfo->pk_attnums[i]));			sprintf(paramname, "$%d", i + 1);			ri_GenerateQual(&querybuf, querysep,							attname, pk_type,							riinfo->pp_eq_oprs[i],							paramname, pk_type);			querysep = "AND";			queryoids[i] = pk_type;		}		appendStringInfo(&querybuf, " FOR SHARE OF x");		/* Prepare and save the plan */		qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,							 &qkey, fk_rel, pk_rel, true);	}	/*	 * We have a plan now. Run it.	 */	result = ri_PerformCheck(&qkey, qplan,							 fk_rel, pk_rel,							 old_row, NULL,							 true,		/* treat like update */							 SPI_OK_SELECT, NULL);	if (SPI_finish() != SPI_OK_FINISH)		elog(ERROR, "SPI_finish failed");	return result;}/* ---------- * RI_FKey_noaction_del - * *	Give an error and roll back the current transaction if the *	delete has resulted in a violation of the given referential *	integrity constraint. * ---------- */DatumRI_FKey_noaction_del(PG_FUNCTION_ARGS){	TriggerData *trigdata = (TriggerData *) fcinfo->context;	RI_ConstraintInfo riinfo;	Relation	fk_rel;	Relation	pk_rel;	HeapTuple	old_row;	RI_QueryKey qkey;	SPIPlanPtr	qplan;	int			i;	/*	 * Check that this is a valid trigger call on the right time and event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);	/*	 * Get arguments.	 */	ri_FetchConstraintInfo(&riinfo,						   trigdata->tg_trigger, trigdata->tg_relation, true);	/*	 * Nothing to do if no column names to compare given	 */	if (riinfo.nkeys == 0)		return PointerGetDatum(NULL);	/*	 * Get the relation descriptors of the FK and PK tables and the old tuple.	 *	 * fk_rel is opened in RowShareLock mode since that's what our eventual	 * SELECT FOR SHARE will get on it.	 */	fk_rel = heap_open(riinfo.fk_relid, RowShareLock);	pk_rel = trigdata->tg_relation;	old_row = trigdata->tg_trigtuple;	if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))	{		/*		 * There's either another row, or no row could match this one.  In		 * either case, we don't need to do the check.		 */		heap_close(fk_rel, RowShareLock);		return PointerGetDatum(NULL);	}	switch (riinfo.confmatchtype)	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 6) a) iv):			 *		MATCH <unspecified> or MATCH FULL			 *			... ON DELETE CASCADE			 * ----------			 */		case FKCONSTR_MATCH_UNSPECIFIED:		case FKCONSTR_MATCH_FULL:			ri_BuildQueryKeyFull(&qkey, &riinfo,								 RI_PLAN_NOACTION_DEL_CHECKREF);			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))			{				case RI_KEYS_ALL_NULL:				case RI_KEYS_SOME_NULL:					/*					 * No check - MATCH FULL means there cannot be any					 * reference to old key if it contains NULL					 */					heap_close(fk_rel, RowShareLock);					return PointerGetDatum(NULL);				case RI_KEYS_NONE_NULL:					/*					 * Have a full qualified key - continue below					 */					break;			}			if (SPI_connect() != SPI_OK_CONNECT)				elog(ERROR, "SPI_connect failed");			/*			 * Fetch or prepare a saved plan for the restrict delete lookup if			 * foreign references exist			 */			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)			{				StringInfoData querybuf;				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				char		paramname[16];				const char *querysep;				Oid			queryoids[RI_MAX_NUMKEYS];				/* ----------				 * The query string built is				 *	SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes.				 * ----------				 */				initStringInfo(&querybuf);				quoteRelationName(fkrelname, fk_rel);				appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",								 fkrelname);				querysep = "WHERE";				for (i = 0; i < riinfo.nkeys; i++)				{					Oid			pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);					Oid			fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);					quoteOneName(attname,								 RIAttName(fk_rel, riinfo.fk_attnums[i]));					sprintf(paramname, "$%d", i + 1);					ri_GenerateQual(&querybuf, querysep,									paramname, pk_type,									riinfo.pf_eq_oprs[i],									attname, fk_type);					querysep = "AND";					queryoids[i] = pk_type;				}				appendStringInfo(&querybuf, " FOR SHARE OF x");				/* Prepare and save the plan */				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,									 &qkey, fk_rel, pk_rel, true);			}			/*			 * We have a plan now. Run it to check for existing references.			 */			ri_PerformCheck(&qkey, qplan,							fk_rel, pk_rel,							old_row, NULL,							true,		/* must detect new rows */							SPI_OK_SELECT,							NameStr(riinfo.conname));			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowShareLock);			return PointerGetDatum(NULL);			/*			 * Handle MATCH PARTIAL restrict delete.			 */		case FKCONSTR_MATCH_PARTIAL:			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("MATCH PARTIAL not yet implemented")));			return PointerGetDatum(NULL);	}	/*	 * Never reached	 */	elog(ERROR, "invalid confmatchtype");	return PointerGetDatum(NULL);}/* ---------- * RI_FKey_noaction_upd - * *	Give an error and roll back the current transaction if the *	update has resulted in a violation of the given referential *	integrity constraint. * ---------- */DatumRI_FKey_noaction_upd(PG_FUNCTION_ARGS){	TriggerData *trigdata = (TriggerData *) fcinfo->context;	RI_ConstraintInfo riinfo;	Relation	fk_rel;	Relation	pk_rel;	HeapTuple	new_row;	HeapTuple	old_row;	RI_QueryKey qkey;	SPIPlanPtr	qplan;	int			i;	/*	 * Check that this is a valid trigger call on the right time and event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);	/*	 * Get arguments.	 */	ri_FetchConstraintInfo(&riinfo,						   trigdata->tg_trigger, trigdata->tg_relation, true);	/*	 * Nothing to do if no column names to compare given	 */	if (riinfo.nkeys == 0)		return PointerGetDatum(NULL);	/*	 * Get the relation descriptors of the FK and PK tables and the new and	 * old tuple.	 *	 * fk_rel is opened in RowShareLock mode since that's what our eventual	 * SELECT FOR SHARE will get on it.	 */	fk_rel = heap_open(riinfo.fk_relid, RowShareLock);	pk_rel = trigdata->tg_relation;	new_row = trigdata->tg_newtuple;	old_row = trigdata->tg_trigtuple;	switch (riinfo.confmatchtype)	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 6) a) iv):			 *		MATCH <unspecified> or MATCH FULL			 *			... ON DELETE CASCADE			 * ----------			 */		case FKCONSTR_MATCH_UNSPECIFIED:		case FKCONSTR_MATCH_FULL:			ri_BuildQueryKeyFull(&qkey, &riinfo,								 RI_PLAN_NOACTION_UPD_CHECKREF);			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))			{				case RI_KEYS_ALL_NULL:				case RI_KEYS_SOME_NULL:					/*					 * No check - MATCH FULL means there cannot be any					 * reference to old key if it contains NULL					 */					heap_close(fk_rel, RowShareLock);					return PointerGetDatum(NULL);				case RI_KEYS_NONE_NULL:					/*					 * Have a full qualified key - continue below					 */					break;			}			/*			 * No need to check anything if old and new keys are equal			 */			if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))			{				heap_close(fk_rel, RowShareLock);				return PointerGetDatum(NULL);			}			if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))			{				/*				 * There's either another row, or no row could match this one.				 * In either case, we don't need to do the check.				 */				heap_close(fk_rel, RowShareLock);				return PointerGetDatum(NULL);			}			if (SPI_connect() != SPI_OK_CONNECT)				elog(ERROR, "SPI_connect failed");			/*			 * Fetch or prepare a saved plan for the noaction update lookup if			 * foreign references exist			 */			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)			{				StringInfoData querybuf;				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				char		paramname[16];				const char *querysep;				Oid			queryoids[RI_MAX_NUMKEYS];				/* ----------				 * The query string built is				 *	SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes.				 * ----------				 */				initStringInfo(&querybuf);				quoteRelationName(fkrelname, fk_rel);				appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",								 fkrelname);				querysep = "WHERE";				for (i = 0; i < riinfo.nkeys; i++)				{					Oid			pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);					Oid			fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);					quoteOneName(attname,								 RIAttName(fk_rel, riinfo.fk_attnums[i]));					sprintf(paramname, "$%d", i + 1);					ri_GenerateQual(&querybuf, querysep,									paramname, pk_type,									riinfo.pf_eq_oprs[i],									attname, fk_type);					querysep = "AND";					queryoids[i] = pk_type;				}				appendStringInfo(&querybuf, " FOR SHARE OF x");				/* Prepare and save the plan */				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,									 &qkey, fk_rel, pk_rel, true);			}

⌨️ 快捷键说明

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