ri_triggers.c

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

C
2,399
字号
}/* ---------- * 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,				  Oid tgoid, int match_type,				  int tgnargs, char **tgargs){	void	   *qplan;	RI_QueryKey qkey;	int			i;	bool		result;	ri_BuildQueryKeyPkCheck(&qkey, tgoid,							RI_PLAN_CHECK_LOOKUPPK, pk_rel,							tgnargs, tgargs);	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 (match_type)			{				case RI_MATCH_TYPE_FULL:				case RI_MATCH_TYPE_UNSPECIFIED:					/*					 * MATCH <unspecified>/FULL  - if ANY column is null,					 * we can't be matching to this row already.					 */					return true;				case RI_MATCH_TYPE_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)	{		char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +							(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];		char		pkrelname[MAX_QUOTED_REL_NAME_LEN];		char		attname[MAX_QUOTED_NAME_LEN];		const char *querysep;		Oid			queryoids[RI_MAX_NUMKEYS];		/* ----------		 * The query string built is		 *	SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...]		 * The type id's for the $ parameters are those of the		 * corresponding FK attributes. Thus, ri_PlanCheck could		 * eventually fail if the parser cannot identify some way		 * how to compare these two types by '='.		 * ----------		 */		quoteRelationName(pkrelname, pk_rel);		snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", pkrelname);		querysep = "WHERE";		for (i = 0; i < qkey.nkeypairs; i++)		{			quoteOneName(attname,			 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_PK_IDX]);			snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",					 querysep, attname, i + 1);			querysep = "AND";			queryoids[i] = SPI_gettypeid(pk_rel->rd_att,									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);		}		strcat(querystr, " FOR UPDATE OF x");		/* Prepare and save the plan */		qplan = ri_PlanCheck(querystr, qkey.nkeypairs, 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;	int			tgnargs;	char	  **tgargs;	Relation	fk_rel;	Relation	pk_rel;	HeapTuple	old_row;	RI_QueryKey qkey;	void	   *qplan;	int			i;	int			match_type;	/*	 * Check that this is a valid trigger call on the right time and	 * event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);	tgnargs = trigdata->tg_trigger->tgnargs;	tgargs = trigdata->tg_trigger->tgargs;	/*	 * Nothing to do if no column names to compare given	 */	if (tgnargs == 4)		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 UPDATE will get on it.	 */	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);	pk_rel = trigdata->tg_relation;	old_row = trigdata->tg_trigtuple;	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);	if (ri_Check_Pk_Match(pk_rel, fk_rel,						  old_row, trigdata->tg_trigger->tgoid,						  match_type, tgnargs, tgargs))	{		/*		 * 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 (match_type)	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 6) a) iv):			 *		MATCH <unspecified> or MATCH FULL			 *			... ON DELETE CASCADE			 * ----------			 */		case RI_MATCH_TYPE_UNSPECIFIED:		case RI_MATCH_TYPE_FULL:			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,								 RI_PLAN_NOACTION_DEL_CHECKREF,								 fk_rel, pk_rel,								 tgnargs, tgargs);			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)			{				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +							(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				const char *querysep;				Oid			queryoids[RI_MAX_NUMKEYS];				/* ----------				 * The query string built is				 *	SELECT 1 FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes. Thus, ri_PlanCheck could				 * eventually fail if the parser cannot identify some way				 * how to compare these two types by '='.				 * ----------				 */				quoteRelationName(fkrelname, fk_rel);				snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", fkrelname);				querysep = "WHERE";				for (i = 0; i < qkey.nkeypairs; i++)				{					quoteOneName(attname,								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);					snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",							 querysep, attname, i + 1);					querysep = "AND";					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);				}				strcat(querystr, " FOR UPDATE OF x");				/* Prepare and save the plan */				qplan = ri_PlanCheck(querystr, qkey.nkeypairs, 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,							tgargs[RI_CONSTRAINT_NAME_ARGNO]);			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 RI_MATCH_TYPE_PARTIAL:			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("MATCH PARTIAL not yet implemented")));			return PointerGetDatum(NULL);	}	/*	 * Never reached	 */	elog(ERROR, "invalid match_type");	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;	int			tgnargs;	char	  **tgargs;	Relation	fk_rel;	Relation	pk_rel;	HeapTuple	new_row;	HeapTuple	old_row;	RI_QueryKey qkey;	void	   *qplan;	int			i;	int			match_type;	/*	 * Check that this is a valid trigger call on the right time and	 * event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);	tgnargs = trigdata->tg_trigger->tgnargs;	tgargs = trigdata->tg_trigger->tgargs;	/*	 * Nothing to do if no column names to compare given	 */	if (tgnargs == 4)		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 UPDATE will get on it.	 */	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);	pk_rel = trigdata->tg_relation;	new_row = trigdata->tg_newtuple;	old_row = trigdata->tg_trigtuple;	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);	switch (match_type)	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 6) a) iv):			 *		MATCH <unspecified> or MATCH FULL			 *			... ON DELETE CASCADE			 * ----------			 */		case RI_MATCH_TYPE_UNSPECIFIED:		case RI_MATCH_TYPE_FULL:			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,								 RI_PLAN_NOACTION_UPD_CHECKREF,								 fk_rel, pk_rel,								 tgnargs, tgargs);			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, &qkey,							 RI_KEYPAIR_PK_IDX))			{				heap_close(fk_rel, RowShareLock);				return PointerGetDatum(NULL);			}			if (ri_Check_Pk_Match(pk_rel, fk_rel,								  old_row, trigdata->tg_trigger->tgoid,								  match_type, tgnargs, tgargs))			{				/*				 * 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)			{				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +							(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				const char *querysep;				Oid			queryoids[RI_MAX_NUMKEYS];				/* ----------				 * The query string built is				 *	SELECT 1 FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes. Thus, ri_PlanCheck could				 * eventually fail if the parser cannot identify some way				 * how to compare these two types by '='.				 * ----------				 */				quoteRelationName(fkrelname, fk_rel);				snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", fkrelname);				querysep = "WHERE";				for (i = 0; i < qkey.nkeypairs; i++)				{					quoteOneName(attname,								 tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);					snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",							 querysep, attname, i + 1);					querysep = "AND";					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);				}				strcat(querystr, " FOR UPDATE OF x");				/* Prepare and save the plan */				qplan = ri_PlanCheck(querystr, qkey.nkeypairs, 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,							tgargs[RI_CONSTRAINT_NAME_ARGNO]);

⌨️ 快捷键说明

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