ri_triggers.c

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

C
2,476
字号
			/*			 * No need to do anything if old and new keys are equal			 */			if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))			{				heap_close(fk_rel, RowExclusiveLock);				return PointerGetDatum(NULL);			}			if (SPI_connect() != SPI_OK_CONNECT)				elog(ERROR, "SPI_connect failed");			/*			 * "MATCH <unspecified>" only changes columns corresponding to the			 * referenced columns that have changed in pk_rel.	This means the			 * "SET attrn=NULL [, attrn=NULL]" string will be change as well.			 * In this case, we need to build a temporary plan rather than use			 * our cached plan, unless the update happens to change all			 * columns in the key.	Fortunately, for the most common case of a			 * single-column foreign key, this will be true.			 *			 * In case you're wondering, the inequality check works because we			 * know that the old key value has no NULLs (see above).			 */			use_cached_query = (riinfo.confmatchtype == FKCONSTR_MATCH_FULL) ||				ri_AllKeysUnequal(pk_rel, old_row, new_row,								  &riinfo, true);			/*			 * Fetch or prepare a saved plan for the set null update operation			 * if possible, or build a temporary plan if not.			 */			if (!use_cached_query ||				(qplan = ri_FetchPreparedPlan(&qkey)) == NULL)			{				StringInfoData querybuf;				StringInfoData qualbuf;				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				char		paramname[16];				const char *querysep;				const char *qualsep;				Oid			queryoids[RI_MAX_NUMKEYS];				/* ----------				 * The query string built is				 *	UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]				 *			WHERE $1 = fkatt1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes.				 * ----------				 */				initStringInfo(&querybuf);				initStringInfo(&qualbuf);				quoteRelationName(fkrelname, fk_rel);				appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);				querysep = "";				qualsep = "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]));					/*					 * MATCH <unspecified> - only change columns corresponding					 * to changed columns in pk_rel's key					 */					if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||						!ri_OneKeyEqual(pk_rel, i, old_row, new_row,										&riinfo, true))					{						appendStringInfo(&querybuf,										 "%s %s = NULL",										 querysep, attname);						querysep = ",";					}					sprintf(paramname, "$%d", i + 1);					ri_GenerateQual(&qualbuf, qualsep,									paramname, pk_type,									riinfo.pf_eq_oprs[i],									attname, fk_type);					qualsep = "AND";					queryoids[i] = pk_type;				}				appendStringInfoString(&querybuf, qualbuf.data);				/*				 * Prepare the plan.  Save it only if we're building the				 * "standard" plan.				 */				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,									 &qkey, fk_rel, pk_rel,									 use_cached_query);			}			/*			 * We have a plan now. Run it to update the existing references.			 */			ri_PerformCheck(&qkey, qplan,							fk_rel, pk_rel,							old_row, NULL,							true,		/* must detect new rows */							SPI_OK_UPDATE,							NameStr(riinfo.conname));			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowExclusiveLock);			return PointerGetDatum(NULL);			/*			 * Handle MATCH PARTIAL set null update.			 */		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_setdefault_del - * *	Set foreign key references to defaults at delete event on PK table. * ---------- */DatumRI_FKey_setdefault_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;	/*	 * Check that this is a valid trigger call on the right time and event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_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 RowExclusiveLock mode since that's what our	 * eventual UPDATE will get on it.	 */	fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);	pk_rel = trigdata->tg_relation;	old_row = trigdata->tg_trigtuple;	switch (riinfo.confmatchtype)	{			/* ----------			 * SQL3 11.9 <referential constraint definition>			 *	Gereral rules 6) a) iii):			 *		MATCH <UNSPECIFIED> or MATCH FULL			 *			... ON DELETE SET DEFAULT			 * ----------			 */		case FKCONSTR_MATCH_UNSPECIFIED:		case FKCONSTR_MATCH_FULL:			ri_BuildQueryKeyFull(&qkey, &riinfo,								 RI_PLAN_SETNULL_DEL_DOUPDATE);			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;			}			if (SPI_connect() != SPI_OK_CONNECT)				elog(ERROR, "SPI_connect failed");			/*			 * Prepare a plan for the set default delete operation.			 * Unfortunately we need to do it on every invocation because the			 * default value could potentially change between calls.			 */			{				StringInfoData querybuf;				StringInfoData qualbuf;				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				char		paramname[16];				const char *querysep;				const char *qualsep;				Oid			queryoids[RI_MAX_NUMKEYS];				int			i;				/* ----------				 * The query string built is				 *	UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]				 *			WHERE $1 = fkatt1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes.				 * ----------				 */				initStringInfo(&querybuf);				initStringInfo(&qualbuf);				quoteRelationName(fkrelname, fk_rel);				appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);				querysep = "";				qualsep = "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]));					appendStringInfo(&querybuf,									 "%s %s = DEFAULT",									 querysep, attname);					sprintf(paramname, "$%d", i + 1);					ri_GenerateQual(&qualbuf, qualsep,									paramname, pk_type,									riinfo.pf_eq_oprs[i],									attname, fk_type);					querysep = ",";					qualsep = "AND";					queryoids[i] = pk_type;				}				appendStringInfoString(&querybuf, qualbuf.data);				/* Prepare the plan, don't save it */				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,									 &qkey, fk_rel, pk_rel, false);			}			/*			 * We have a plan now. Run it to update the existing references.			 */			ri_PerformCheck(&qkey, qplan,							fk_rel, pk_rel,							old_row, NULL,							true,		/* must detect new rows */							SPI_OK_UPDATE,							NameStr(riinfo.conname));			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowExclusiveLock);			/*			 * In the case we delete the row who's key is equal to the default			 * values AND a referencing row in the foreign key table exists,			 * we would just have updated it to the same values. We need to do			 * another lookup now and in case a reference exists, abort the			 * operation. That is already implemented in the NO ACTION			 * trigger.			 */			RI_FKey_noaction_del(fcinfo);			return PointerGetDatum(NULL);			/*			 * Handle MATCH PARTIAL set null 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_setdefault_upd - * *	Set foreign key references to defaults at update event on PK table. * ---------- */DatumRI_FKey_setdefault_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;	/*	 * Check that this is a valid trigger call on the right time and event.	 */	ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_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 old tuple.	 *	 * fk_rel is opened in RowExclusiveLock mode since that's what our	 * eventual UPDATE will get on it.	 */	fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);	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 7) a) iii):			 *		MATCH <UNSPECIFIED> or MATCH FULL			 *			... ON UPDATE SET DEFAULT			 * ----------			 */		case FKCONSTR_MATCH_UNSPECIFIED:		case FKCONSTR_MATCH_FULL:			ri_BuildQueryKeyFull(&qkey, &riinfo,								 RI_PLAN_SETNULL_DEL_DOUPDATE);			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, &riinfo, true))			{				heap_close(fk_rel, RowExclusiveLock);				return PointerGetDatum(NULL);			}			if (SPI_connect() != SPI_OK_CONNECT)				elog(ERROR, "SPI_connect failed");			/*			 * Prepare a plan for the set default delete operation.			 * Unfortunately we need to do it on every invocation because the			 * default value could potentially change between calls.			 */			{				StringInfoData querybuf;				StringInfoData qualbuf;				char		fkrelname[MAX_QUOTED_REL_NAME_LEN];				char		attname[MAX_QUOTED_NAME_LEN];				char		paramname[16];				const char *querysep;				const char *qualsep;				Oid			queryoids[RI_MAX_NUMKEYS];				int			i;				/* ----------				 * The query string built is				 *	UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]				 *			WHERE $1 = fkatt1 [AND ...]				 * The type id's for the $ parameters are those of the				 * corresponding PK attributes.				 * ----------				 */				initStringInfo(&querybuf);				initStringInfo(&qualbuf);				quoteRelationName(fkrelname, fk_rel);				appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);				querysep = "";				qualsep = "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]));					/*					 * MATCH <unspecified> - only change columns corresponding					 * to changed columns in pk_rel's key					 */					if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||						!ri_OneKeyEqual(pk_rel, i, old_row, new_row,										&riinfo, true))					{						appendStringInfo(&querybuf,										 "%s %s = DEFAULT",										 querysep, attname);						querysep = ",";					}					sprintf(paramname, "$%d", i + 1);					ri_GenerateQual(&qualbuf, qualsep,									paramname, pk_type,									riinfo.pf_eq_oprs[i],									attname, fk_type);					qualsep = "AND";					queryoids[i] = pk_type;				}				appendStringInfoString(&querybuf, qualbuf.data);				/* Prepare the plan, don't save it */				qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,									 &qkey, fk_rel, pk_rel, false);			}			/*			 * We have a plan now. Run it to update the existing references.			 */			ri_PerformCheck(&qkey, qplan,							fk_rel, pk_rel,							old_row, NULL,							true,		/* must detect new rows */							SPI_OK_UPDATE,							NameStr(riinfo.conname));			if (SPI_finish() != SPI_OK_FINISH)				elog(ERROR, "SPI_finish failed");			heap_close(fk_rel, RowExclusiveLock);			/*			 * In the case we updated th

⌨️ 快捷键说明

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