ri_triggers.c

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

C
2,399
字号
			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowShareLock);			return PointerGetDatum(NULL);			/*			 * Handle MATCH PARTIAL noaction update.			 */		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_cascade_del - * *	Cascaded delete foreign key references at delete event on PK table. * ---------- */DatumRI_FKey_cascade_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;	/*	 * Check that this is a valid trigger call on the right time and	 * event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_cascade_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 RowExclusiveLock mode since that's what our	 * eventual DELETE will get on it.	 */	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);	pk_rel = trigdata->tg_relation;	old_row = trigdata->tg_trigtuple;	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 6) a) i):			 *		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_CASCADE_DEL_DODELETE,								 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, RowExclusiveLock);					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 cascaded delete			 */			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				 *	DELETE 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), "DELETE FROM ONLY %s", 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]);				}				/* Prepare and save the plan */				qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,									 &qkey, fk_rel, pk_rel, true);			}			/*			 * We have a plan now. Build up the arguments from the key			 * values in the deleted PK tuple and delete the referencing			 * rows			 */			ri_PerformCheck(&qkey, qplan,							fk_rel, pk_rel,							old_row, NULL,							true, /* must detect new rows */							SPI_OK_DELETE,							tgargs[RI_CONSTRAINT_NAME_ARGNO]);			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowExclusiveLock);			return PointerGetDatum(NULL);			/*			 * Handle MATCH PARTIAL cascaded 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_cascade_upd - * *	Cascaded update/delete foreign key references at update event on PK table. * ---------- */DatumRI_FKey_cascade_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			j;	/*	 * Check that this is a valid trigger call on the right time and	 * event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_cascade_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 RowExclusiveLock mode since that's what our	 * eventual UPDATE will get on it.	 */	fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);	pk_rel = trigdata->tg_relation;	new_row = trigdata->tg_newtuple;	old_row = trigdata->tg_trigtuple;	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 7) a) i):			 *		MATCH <unspecified> or MATCH FULL			 *			... ON UPDATE CASCADE			 * ----------			 */		case RI_MATCH_TYPE_UNSPECIFIED:		case RI_MATCH_TYPE_FULL:			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,								 RI_PLAN_CASCADE_UPD_DOUPDATE,								 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 update - MATCH FULL means there cannot be any					 * reference to old key if it contains NULL					 */					heap_close(fk_rel, RowExclusiveLock);					return PointerGetDatum(NULL);				case RI_KEYS_NONE_NULL:					/*					 * Have a full qualified key - continue below					 */					break;			}			/*			 * No need to do 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, RowExclusiveLock);				return PointerGetDatum(NULL);			}			if (SPI_connect() != SPI_OK_CONNECT)				elog(ERROR, "SPI_connect failed");			/*			 * Fetch or prepare a saved plan for the cascaded update of			 * foreign references			 */			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)			{				char		querystr[MAX_QUOTED_REL_NAME_LEN + 100 +												 (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];				char		qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				const char *querysep;				const char *qualsep;				Oid			queryoids[RI_MAX_NUMKEYS * 2];				/* ----------				 * The query string built is				 *	UPDATE ONLY <fktable> SET fkatt1 = $1 [, ...]				 *			WHERE fkatt1 = $n [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), "UPDATE ONLY %s SET", fkrelname);				qualstr[0] = '\0';				querysep = "";				qualsep = "WHERE";				for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)				{					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);					snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d",							 qualsep, attname, j + 1);					querysep = ",";					qualsep = "AND";					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);					queryoids[j] = queryoids[i];				}				strcat(querystr, qualstr);				/* Prepare and save the plan */				qplan = ri_PlanCheck(querystr, qkey.nkeypairs * 2, queryoids,									 &qkey, fk_rel, pk_rel, true);			}			/*			 * We have a plan now. Run it to update the existing			 * references.			 */			ri_PerformCheck(&qkey, qplan,							fk_rel, pk_rel,							old_row, new_row,							true, /* must detect new rows */							SPI_OK_UPDATE,							tgargs[RI_CONSTRAINT_NAME_ARGNO]);			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowExclusiveLock);			return PointerGetDatum(NULL);			/*			 * Handle MATCH PARTIAL cascade update.			 */		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_restrict_del - * *	Restrict delete from PK table to rows unreferenced by foreign key. * *	SQL3 intends that this referential action occur BEFORE the *	update is performed, rather than after.  This appears to be *	the only difference between "NO ACTION" and "RESTRICT". * *	For now, however, we treat "RESTRICT" and "NO ACTION" as *	equivalent. * ---------- */DatumRI_FKey_restrict_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;	/*	 * Check that this is a valid trigger call on the right time and	 * event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_restrict_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;	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))	{			/* ----------			 * 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_RESTRICT_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

⌨️ 快捷键说明

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