ri_triggers.c 83.5 KB
Newer Older
1 2 3 4 5 6
/* ----------
 * ri_triggers.c
 *
 *	Generic trigger procedures for referential integrity constraint
 *	checks.
 *
7 8 9 10 11
 *	Note about memory management: the private hashtables kept here live
 *	across query and transaction boundaries, in fact they live as long as
 *	the backend does.  This works because the hashtable structures
 *	themselves are allocated by dynahash.c in its permanent DynaHashCxt,
 *	and the parse/plan node trees they point to are copied into
12
 *	TopMemoryContext using SPI_saveplan().	This is pretty ugly, since there
13 14 15
 *	is no way to free a no-longer-needed plan tree, but then again we don't
 *	yet have any bookkeeping that would allow us to detect that a plan isn't
 *	needed anymore.  Improve it someday.
16
 *
17 18 19 20
 *
 * Portions Copyright (c) 2000-2001, PostgreSQL Global Development Group
 * Copyright 1999 Jan Wieck
 *
21
 * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.30 2001/11/12 00:46:36 tgl Exp $
22 23 24 25
 *
 * ----------
 */

Jan Wieck's avatar
Jan Wieck committed
26 27 28 29

/* ----------
 * Internal TODO:
 *
Jan Wieck's avatar
Jan Wieck committed
30
 *		Add MATCH PARTIAL logic.
Jan Wieck's avatar
Jan Wieck committed
31 32 33
 * ----------
 */

34 35
#include "postgres.h"

36
#include "catalog/pg_operator.h"
37
#include "commands/trigger.h"
38
#include "executor/spi_priv.h"
39
#include "miscadmin.h"
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58


/* ----------
 * Local definitions
 * ----------
 */

#define RI_INIT_QUERYHASHSIZE			128
#define RI_INIT_OPREQHASHSIZE			128

#define RI_MATCH_TYPE_UNSPECIFIED		0
#define RI_MATCH_TYPE_FULL				1
#define RI_MATCH_TYPE_PARTIAL			2

#define RI_KEYS_ALL_NULL				0
#define RI_KEYS_SOME_NULL				1
#define RI_KEYS_NONE_NULL				2


Jan Wieck's avatar
Jan Wieck committed
59 60 61 62
#define RI_PLAN_CHECK_LOOKUPPK_NOCOLS	1
#define RI_PLAN_CHECK_LOOKUPPK			2
#define RI_PLAN_CASCADE_DEL_DODELETE	1
#define RI_PLAN_CASCADE_UPD_DOUPDATE	1
63 64
#define RI_PLAN_NOACTION_DEL_CHECKREF	1
#define RI_PLAN_NOACTION_UPD_CHECKREF	1
Jan Wieck's avatar
Jan Wieck committed
65 66
#define RI_PLAN_RESTRICT_DEL_CHECKREF	1
#define RI_PLAN_RESTRICT_UPD_CHECKREF	1
Jan Wieck's avatar
Jan Wieck committed
67 68
#define RI_PLAN_SETNULL_DEL_DOUPDATE	1
#define RI_PLAN_SETNULL_UPD_DOUPDATE	1
69 70 71 72 73 74 75 76


/* ----------
 * RI_QueryKey
 *
 *	The key identifying a prepared SPI plan in our private hashtable
 * ----------
 */
77 78 79 80 81 82 83 84 85
typedef struct RI_QueryKey
{
	int32		constr_type;
	Oid			constr_id;
	int32		constr_queryno;
	Oid			fk_relid;
	Oid			pk_relid;
	int32		nkeypairs;
	int16		keypair[RI_MAX_NUMKEYS][2];
86 87 88 89 90 91 92
} RI_QueryKey;


/* ----------
 * RI_QueryHashEntry
 * ----------
 */
93 94 95 96
typedef struct RI_QueryHashEntry
{
	RI_QueryKey key;
	void	   *plan;
97 98 99
} RI_QueryHashEntry;


100 101 102 103
typedef struct RI_OpreqHashEntry
{
	Oid			typeid;
	FmgrInfo	oprfmgrinfo;
104 105 106 107 108 109 110 111
} RI_OpreqHashEntry;



/* ----------
 * Local data
 * ----------
 */
112 113
static HTAB *ri_query_cache = (HTAB *) NULL;
static HTAB *ri_opreq_cache = (HTAB *) NULL;
114 115 116 117 118 119


/* ----------
 * Local function prototypes
 * ----------
 */
120 121 122
static int	ri_DetermineMatchType(char *str);
static int ri_NullCheck(Relation rel, HeapTuple tup,
			 RI_QueryKey *key, int pairidx);
123
static void ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id,
124 125 126 127 128 129 130
					 int32 constr_queryno,
					 Relation fk_rel, Relation pk_rel,
					 int argc, char **argv);
static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
			 RI_QueryKey *key, int pairidx);
static bool ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
				  RI_QueryKey *key, int pairidx);
131
static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
132
			   HeapTuple newtup, RI_QueryKey *key, int pairidx);
133 134 135 136 137 138 139 140 141 142 143
static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);

static void ri_InitHashTables(void);
static void *ri_FetchPreparedPlan(RI_QueryKey *key);
static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);



/* ----------
 * RI_FKey_check -
 *
144
 *	Check foreign key existence (combined for INSERT and UPDATE).
145 146
 * ----------
 */
147 148
static Datum
RI_FKey_check(PG_FUNCTION_ARGS)
149
{
150
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
151 152 153 154 155 156 157 158 159 160 161 162 163
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		check_values[RI_MAX_NUMKEYS];
	char		check_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
	int			match_type;
164 165 166
	Oid			save_uid;

	save_uid = GetUserId();
167

168
	ReferentialIntegritySnapshotOverride = true;
169

170 171 172
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
173
	 */
174
	if (!CALLED_AS_TRIGGER(fcinfo))
175
		elog(ERROR, "RI_FKey_check() not fired by trigger manager");
176 177
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
178 179
		elog(ERROR, "RI_FKey_check() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
180
		!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
181 182
		elog(ERROR, "RI_FKey_check() must be fired for INSERT or UPDATE");

183
	/*
184
	 * Check for the correct # of call arguments
185 186
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
187
	tgargs = trigdata->tg_trigger->tgargs;
188 189 190 191
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_check()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()",
192
			 RI_MAX_NUMKEYS);
193

194 195 196
	/*
	 * Get the relation descriptors of the FK and PK tables and the new
	 * tuple.
197
	 */
198 199
	fk_rel = trigdata->tg_relation;
	pk_rel = heap_openr(tgargs[RI_PK_RELNAME_ARGNO], NoLock);
200 201 202 203
	if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
	{
		old_row = trigdata->tg_trigtuple;
		new_row = trigdata->tg_newtuple;
204 205 206
	}
	else
	{
207 208 209 210 211 212 213 214 215
		old_row = NULL;
		new_row = trigdata->tg_trigtuple;
	}

	/* ----------
	 * SQL3 11.9 <referential constraint definition>
	 *	Gereral rules 2) a):
	 *		If Rf and Rt are empty (no columns to compare given)
	 *		constraint is true if 0 < (SELECT COUNT(*) FROM T)
Jan Wieck's avatar
Jan Wieck committed
216 217 218 219
	 *
	 *	Note: The special case that no columns are given cannot
	 *		occur up to now in Postgres, it's just there for
	 *		future enhancements.
220 221
	 * ----------
	 */
222 223
	if (tgnargs == 4)
	{
Jan Wieck's avatar
Jan Wieck committed
224
		ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
225 226 227
							 RI_PLAN_CHECK_LOOKUPPK_NOCOLS,
							 fk_rel, pk_rel,
							 tgnargs, tgargs);
228 229 230 231 232

		if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
		{
			char		querystr[8192];

233
			/* ---------
234
			 * The query string built is
235
			 *	SELECT oid FROM ONLY <pktable>
236 237
			 * ----------
			 */
238
			sprintf(querystr, "SELECT oid FROM ONLY \"%s\" FOR UPDATE OF \"%s\"",
239 240
					tgargs[RI_PK_RELNAME_ARGNO],
					tgargs[RI_PK_RELNAME_ARGNO]);
241

242
			/*
243 244 245 246 247 248 249
			 * Prepare, save and remember the new plan.
			 */
			qplan = SPI_prepare(querystr, 0, NULL);
			qplan = SPI_saveplan(qplan);
			ri_HashPreparedPlan(&qkey, qplan);
		}

250
		/*
251 252 253 254 255
		 * Execute the plan
		 */
		if (SPI_connect() != SPI_OK_CONNECT)
			elog(NOTICE, "SPI_connect() failed in RI_FKey_check()");

256
		SetUserId(RelationGetForm(pk_rel)->relowner);
257 258
		/* pk_rel is no longer neede OK ? */
		heap_close(pk_rel, NoLock);
259

260 261
		if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
			elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
262

263 264
		SetUserId(save_uid);

265 266
		if (SPI_processed == 0)
			elog(ERROR, "%s referential integrity violation - "
267 268 269
				 "no rows found in %s",
				 tgargs[RI_CONSTRAINT_NAME_ARGNO],
				 tgargs[RI_PK_RELNAME_ARGNO]);
270 271 272 273

		if (SPI_finish() != SPI_OK_FINISH)
			elog(NOTICE, "SPI_finish() failed in RI_FKey_check()");

274
		return PointerGetDatum(NULL);
275 276 277

	}

278 279 280 281 282
	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);

	if (match_type == RI_MATCH_TYPE_PARTIAL)
	{
		elog(ERROR, "MATCH PARTIAL not yet supported");
283
		return PointerGetDatum(NULL);
284 285 286
	}

	ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
287 288
						 RI_PLAN_CHECK_LOOKUPPK, fk_rel, pk_rel,
						 tgnargs, tgargs);
289 290 291 292

	switch (ri_NullCheck(fk_rel, new_row, &qkey, RI_KEYPAIR_FK_IDX))
	{
		case RI_KEYS_ALL_NULL:
293 294 295 296

			/*
			 * No check - if NULLs are allowed at all is already checked
			 * by NOT NULL constraint.
297
			 *
298 299
			 * This is true for MATCH FULL, MATCH PARTIAL, and MATCH
			 * <unspecified>
300 301
			 */
			heap_close(pk_rel, NoLock);
302
			return PointerGetDatum(NULL);
303

304
		case RI_KEYS_SOME_NULL:
305 306 307 308

			/*
			 * This is the only case that differs between the three kinds
			 * of MATCH.
309 310 311 312
			 */
			switch (match_type)
			{
				case RI_MATCH_TYPE_FULL:
313 314 315 316

					/*
					 * Not allowed - MATCH FULL says either all or none of
					 * the attributes can be NULLs
317 318
					 */
					elog(ERROR, "%s referential integrity violation - "
319 320 321
						 "MATCH FULL doesn't allow mixing of NULL "
						 "and NON-NULL key values",
						 tgargs[RI_CONSTRAINT_NAME_ARGNO]);
322
					heap_close(pk_rel, NoLock);
323
					return PointerGetDatum(NULL);
324 325

				case RI_MATCH_TYPE_UNSPECIFIED:
326 327

					/*
328 329 330 331
					 * MATCH <unspecified> - if ANY column is null, we
					 * have a match.
					 */
					heap_close(pk_rel, NoLock);
332
					return PointerGetDatum(NULL);
333 334

				case RI_MATCH_TYPE_PARTIAL:
335 336

					/*
337
					 * MATCH PARTIAL - all non-null columns must match.
338 339
					 * (not implemented, can be done by modifying the
					 * query below to only include non-null columns, or by
340
					 * writing a special version here)
341 342 343
					 */
					elog(ERROR, "MATCH PARTIAL not yet implemented");
					heap_close(pk_rel, NoLock);
344
					return PointerGetDatum(NULL);
345 346 347
			}

		case RI_KEYS_NONE_NULL:
348 349

			/*
350
			 * Have a full qualified key - continue below for all three
351
			 * kinds of MATCH.
352 353 354 355
			 */
			break;
	}

356 357 358 359 360 361 362
	/*
	 * Note: We cannot avoid the check on UPDATE, even if old and new key
	 * are the same. Otherwise, someone could DELETE the PK that consists
	 * of the DEFAULT values, and if there are any references, a ON DELETE
	 * SET DEFAULT action would update the references to exactly these
	 * values but we wouldn't see that weired case (this is the only place
	 * to see it).
363 364 365 366
	 */
	if (SPI_connect() != SPI_OK_CONNECT)
		elog(NOTICE, "SPI_connect() failed in RI_FKey_check()");

367
	/*
368 369 370 371 372 373
	 * Fetch or prepare a saved plan for the real check
	 */
	if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
	{
		char		buf[256];
		char		querystr[8192];
374
		char	   *querysep;
375 376 377 378
		Oid			queryoids[RI_MAX_NUMKEYS];

		/* ----------
		 * The query string built is
379
		 *	SELECT oid FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...]
380 381 382 383 384 385
		 * The type id's for the $ parameters are those of the
		 * corresponding FK attributes. Thus, SPI_prepare could
		 * eventually fail if the parser cannot identify some way
		 * how to compare these two types by '='.
		 * ----------
		 */
386
		sprintf(querystr, "SELECT oid FROM ONLY \"%s\"",
387
				tgargs[RI_PK_RELNAME_ARGNO]);
388 389 390
		querysep = "WHERE";
		for (i = 0; i < qkey.nkeypairs; i++)
		{
391 392
			sprintf(buf, " %s \"%s\" = $%d", querysep,
					tgargs[5 + i * 2], i + 1);
393 394 395
			strcat(querystr, buf);
			querysep = "AND";
			queryoids[i] = SPI_gettypeid(fk_rel->rd_att,
396
									 qkey.keypair[i][RI_KEYPAIR_FK_IDX]);
397 398
		}
		sprintf(buf, " FOR UPDATE OF \"%s\"",
399
				tgargs[RI_PK_RELNAME_ARGNO]);
400 401
		strcat(querystr, buf);

402
		/*
403 404 405 406 407 408 409
		 * Prepare, save and remember the new plan.
		 */
		qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
		qplan = SPI_saveplan(qplan);
		ri_HashPreparedPlan(&qkey, qplan);
	}

410 411 412
	/*
	 * We have a plan now. Build up the arguments for SPI_execp() from the
	 * key values in the new FK tuple.
413 414 415
	 */
	for (i = 0; i < qkey.nkeypairs; i++)
	{
416
		/*
417
		 * We can implement MATCH PARTIAL by excluding this column from
418
		 * the query if it is null.  Simple!  Unfortunately, the
419 420
		 * referential actions aren't so I've not bothered to do so for
		 * the moment.
421
		 */
422

423
		check_values[i] = SPI_getbinval(new_row,
424 425 426 427
										fk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_FK_IDX],
										&isnull);
		if (isnull)
428 429 430 431 432 433
			check_nulls[i] = 'n';
		else
			check_nulls[i] = ' ';
	}
	check_nulls[i] = '\0';

434
	/*
435 436
	 * Now check that foreign key exists in PK table
	 */
437 438

	SetUserId(RelationGetForm(pk_rel)->relowner);
439 440
	/* pk_rel is no longer needed OK ? */
	heap_close(pk_rel, NoLock);
441

442 443
	if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
		elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
444

445 446
	SetUserId(save_uid);

447 448
	if (SPI_processed == 0)
		elog(ERROR, "%s referential integrity violation - "
449 450 451 452
			 "key referenced from %s not found in %s",
			 tgargs[RI_CONSTRAINT_NAME_ARGNO],
			 tgargs[RI_FK_RELNAME_ARGNO],
			 tgargs[RI_PK_RELNAME_ARGNO]);
453 454 455 456

	if (SPI_finish() != SPI_OK_FINISH)
		elog(NOTICE, "SPI_finish() failed in RI_FKey_check()");

457
	return PointerGetDatum(NULL);
458

459
	/*
460 461 462
	 * Never reached
	 */
	elog(ERROR, "internal error #1 in ri_triggers.c");
463
	return PointerGetDatum(NULL);
464 465 466 467 468 469
}


/* ----------
 * RI_FKey_check_ins -
 *
470
 *	Check foreign key existence at insert event on FK table.
471 472
 * ----------
 */
473 474
Datum
RI_FKey_check_ins(PG_FUNCTION_ARGS)
475
{
476
	return RI_FKey_check(fcinfo);
477 478 479 480 481 482
}


/* ----------
 * RI_FKey_check_upd -
 *
483
 *	Check foreign key existence at update event on FK table.
484 485
 * ----------
 */
486 487
Datum
RI_FKey_check_upd(PG_FUNCTION_ARGS)
488
{
489
	return RI_FKey_check(fcinfo);
490 491 492 493 494 495 496
}


/* ----------
 * RI_FKey_noaction_del -
 *
 *	Give an error and roll back the current transaction if the
497 498
 *	delete has resulted in a violation of the given referential
 *	integrity constraint.
499 500
 * ----------
 */
501 502
Datum
RI_FKey_noaction_del(PG_FUNCTION_ARGS)
503
{
504
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
505 506 507 508 509 510 511 512 513 514 515
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		del_values[RI_MAX_NUMKEYS];
	char		del_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
516
	Oid			save_uid;
517 518

	save_uid = GetUserId();
519 520 521

	ReferentialIntegritySnapshotOverride = true;

522 523 524
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
525
	 */
526
	if (!CALLED_AS_TRIGGER(fcinfo))
527
		elog(ERROR, "RI_FKey_noaction_del() not fired by trigger manager");
528 529
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
530 531 532 533
		elog(ERROR, "RI_FKey_noaction_del() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_noaction_del() must be fired for DELETE");

534
	/*
535
	 * Check for the correct # of call arguments
536 537
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
538
	tgargs = trigdata->tg_trigger->tgargs;
539 540 541 542
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()",
543
			 RI_MAX_NUMKEYS);
544

545
	/*
546 547 548
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
549
		return PointerGetDatum(NULL);
550

551 552 553
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
554
	 */
555 556
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
	pk_rel = trigdata->tg_relation;
557 558
	old_row = trigdata->tg_trigtuple;

559 560
	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
561 562 563 564 565 566 567
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) iv):
			 *		MATCH <unspecified> or MATCH FULL
			 *			... ON DELETE CASCADE
			 * ----------
			 */
568
		case RI_MATCH_TYPE_UNSPECIFIED:
569 570
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
571 572 573
								 RI_PLAN_NOACTION_DEL_CHECKREF,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
574 575 576 577 578

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
579 580

					/*
581 582 583 584
					 * No check - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
585
					return PointerGetDatum(NULL);
586

587
				case RI_KEYS_NONE_NULL:
588 589

					/*
590 591 592 593 594 595 596 597 598
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_noaction_del()");

599
			/*
600 601 602 603 604 605 606
			 * Fetch or prepare a saved plan for the restrict delete
			 * lookup if foreign references exist
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
607
				char	   *querysep;
608 609 610 611
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
612
				 *	SELECT oid FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]
613 614 615 616 617 618
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
619
				sprintf(querystr, "SELECT oid FROM ONLY \"%s\"",
620
						tgargs[RI_FK_RELNAME_ARGNO]);
621 622 623
				querysep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
624 625
					sprintf(buf, " %s \"%s\" = $%d", querysep,
							tgargs[4 + i * 2], i + 1);
626 627 628
					strcat(querystr, buf);
					querysep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
629
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
630
				}
631 632
				sprintf(buf, " FOR UPDATE OF \"%s\"",
						tgargs[RI_FK_RELNAME_ARGNO]);
633 634
				strcat(querystr, buf);

635
				/*
636 637 638 639 640 641 642
				 * Prepare, save and remember the new plan.
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

643
			/*
644 645 646 647 648 649
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the deleted PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				del_values[i] = SPI_getbinval(old_row,
650 651 652 653
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
654 655 656 657 658 659
					del_nulls[i] = 'n';
				else
					del_nulls[i] = ' ';
			}
			del_nulls[i] = '\0';

660
			/*
661 662
			 * Now check for existing references
			 */
663 664
			SetUserId(RelationGetForm(pk_rel)->relowner);

665 666
			if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
				elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");
667

668 669
			SetUserId(save_uid);

670 671
			if (SPI_processed > 0)
				elog(ERROR, "%s referential integrity violation - "
672 673 674 675
					 "key in %s still referenced from %s",
					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
					 tgargs[RI_PK_RELNAME_ARGNO],
					 tgargs[RI_FK_RELNAME_ARGNO]);
676 677 678 679

			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_noaction_del()");

680
			return PointerGetDatum(NULL);
681

682
			/*
683 684
			 * Handle MATCH PARTIAL restrict delete.
			 */
685 686
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
687
			return PointerGetDatum(NULL);
688 689
	}

690
	/*
691 692 693
	 * Never reached
	 */
	elog(ERROR, "internal error #2 in ri_triggers.c");
694
	return PointerGetDatum(NULL);
695 696 697 698 699 700 701
}


/* ----------
 * RI_FKey_noaction_upd -
 *
 *	Give an error and roll back the current transaction if the
702 703
 *	update has resulted in a violation of the given referential
 *	integrity constraint.
704 705
 * ----------
 */
706 707
Datum
RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
708
{
709
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
710 711 712 713 714 715 716 717 718 719 720 721
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS];
	char		upd_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
722
	Oid			save_uid;
723 724

	save_uid = GetUserId();
725 726 727

	ReferentialIntegritySnapshotOverride = true;

728 729 730
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
731
	 */
732
	if (!CALLED_AS_TRIGGER(fcinfo))
733
		elog(ERROR, "RI_FKey_noaction_upd() not fired by trigger manager");
734 735
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
736 737 738 739
		elog(ERROR, "RI_FKey_noaction_upd() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_noaction_upd() must be fired for UPDATE");

740
	/*
741
	 * Check for the correct # of call arguments
742 743
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
744
	tgargs = trigdata->tg_trigger->tgargs;
745 746 747 748
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()",
749
			 RI_MAX_NUMKEYS);
750

751
	/*
752 753 754
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
755
		return PointerGetDatum(NULL);
756

757 758 759
	/*
	 * Get the relation descriptors of the FK and PK tables and the new
	 * and old tuple.
760
	 */
761 762
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
	pk_rel = trigdata->tg_relation;
763 764
	new_row = trigdata->tg_newtuple;
	old_row = trigdata->tg_trigtuple;
765

766 767
	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
768 769 770 771 772 773 774
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) iv):
			 *		MATCH <unspecified> or MATCH FULL
			 *			... ON DELETE CASCADE
			 * ----------
			 */
775
		case RI_MATCH_TYPE_UNSPECIFIED:
776
		case RI_MATCH_TYPE_FULL:
Jan Wieck's avatar
Jan Wieck committed
777
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
778 779 780
								 RI_PLAN_NOACTION_UPD_CHECKREF,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
781

782
			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
783 784
			{
				case RI_KEYS_ALL_NULL:
785
				case RI_KEYS_SOME_NULL:
786 787

					/*
788 789
					 * No check - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
790
					 */
791
					heap_close(fk_rel, NoLock);
792
					return PointerGetDatum(NULL);
793

794
				case RI_KEYS_NONE_NULL:
795 796

					/*
797 798 799 800
					 * Have a full qualified key - continue below
					 */
					break;
			}
801
			heap_close(fk_rel, NoLock);
802

803
			/*
804
			 * No need to check anything if old and new keys are equal
805
			 */
806
			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
807
							 RI_KEYPAIR_PK_IDX))
808
				return PointerGetDatum(NULL);
809

810
			if (SPI_connect() != SPI_OK_CONNECT)
811
				elog(NOTICE, "SPI_connect() failed in RI_FKey_noaction_upd()");
812

813
			/*
814 815
			 * Fetch or prepare a saved plan for the noaction update
			 * lookup if foreign references exist
816 817 818 819 820
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
821
				char	   *querysep;
822 823 824 825
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
826
				 *	SELECT oid FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]
827
				 * The type id's for the $ parameters are those of the
828
				 * corresponding PK attributes. Thus, SPI_prepare could
829 830 831 832
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
833
				sprintf(querystr, "SELECT oid FROM ONLY \"%s\"",
834
						tgargs[RI_FK_RELNAME_ARGNO]);
835 836 837
				querysep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
838 839
					sprintf(buf, " %s \"%s\" = $%d", querysep,
							tgargs[4 + i * 2], i + 1);
840 841
					strcat(querystr, buf);
					querysep = "AND";
842
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
843
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
844
				}
845 846
				sprintf(buf, " FOR UPDATE OF \"%s\"",
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
847
				strcat(querystr, buf);
848

849
				/*
850 851 852 853 854 855 856
				 * Prepare, save and remember the new plan.
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

857
			/*
858
			 * We have a plan now. Build up the arguments for SPI_execp()
859
			 * from the key values in the updated PK tuple.
860 861 862
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
863
				upd_values[i] = SPI_getbinval(old_row,
864 865 866 867
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
868
					upd_nulls[i] = 'n';
869
				else
870
					upd_nulls[i] = ' ';
871
			}
872
			upd_nulls[i] = '\0';
873

874
			/*
875
			 * Now check for existing references
876
			 */
877 878
			SetUserId(RelationGetForm(pk_rel)->relowner);

879 880
			if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
				elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");
881

882 883
			SetUserId(save_uid);

884
			if (SPI_processed > 0)
885
				elog(ERROR, "%s referential integrity violation - "
886 887 888 889
					 "key in %s still referenced from %s",
					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
					 tgargs[RI_PK_RELNAME_ARGNO],
					 tgargs[RI_FK_RELNAME_ARGNO]);
890 891

			if (SPI_finish() != SPI_OK_FINISH)
892 893
				elog(NOTICE, "SPI_finish() failed in RI_FKey_noaction_upd()");

894
			return PointerGetDatum(NULL);
895

896
			/*
897 898
			 * Handle MATCH PARTIAL noaction update.
			 */
899 900
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
901
			return PointerGetDatum(NULL);
902 903
	}

904
	/*
905 906
	 * Never reached
	 */
907
	elog(ERROR, "internal error #3 in ri_triggers.c");
908
	return PointerGetDatum(NULL);
909 910 911
}


912 913 914 915 916 917
/* ----------
 * RI_FKey_cascade_del -
 *
 *	Cascaded delete foreign key references at delete event on PK table.
 * ----------
 */
918 919
Datum
RI_FKey_cascade_del(PG_FUNCTION_ARGS)
920
{
921
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
922 923 924 925 926 927 928 929 930 931 932
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		del_values[RI_MAX_NUMKEYS];
	char		del_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
933 934
	Oid			save_uid;
	Oid			fk_owner;
935

936
	ReferentialIntegritySnapshotOverride = true;
937

938 939 940
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
941
	 */
942
	if (!CALLED_AS_TRIGGER(fcinfo))
943
		elog(ERROR, "RI_FKey_cascade_del() not fired by trigger manager");
944 945
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
946 947 948 949
		elog(ERROR, "RI_FKey_cascade_del() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_cascade_del() must be fired for DELETE");

950
	/*
951
	 * Check for the correct # of call arguments
952 953
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
954
	tgargs = trigdata->tg_trigger->tgargs;
955 956 957 958
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()",
959
			 RI_MAX_NUMKEYS);
960

961
	/*
962 963 964
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
965
		return PointerGetDatum(NULL);
966

967 968 969
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
970
	 */
971
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
972
	fk_owner = RelationGetForm(fk_rel)->relowner;
973
	pk_rel = trigdata->tg_relation;
974 975 976 977
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
978 979 980 981 982 983 984
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) i):
			 *		MATCH <unspecified> or MATCH FULL
			 *			... ON DELETE CASCADE
			 * ----------
			 */
985 986
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
Jan Wieck's avatar
Jan Wieck committed
987
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
988 989 990
								 RI_PLAN_CASCADE_DEL_DODELETE,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
991 992 993 994 995

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
996 997

					/*
998 999 1000 1001
					 * No check - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
1002
					return PointerGetDatum(NULL);
1003

1004
				case RI_KEYS_NONE_NULL:
1005 1006

					/*
1007 1008 1009 1010 1011 1012 1013
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

			if (SPI_connect() != SPI_OK_CONNECT)
Jan Wieck's avatar
Jan Wieck committed
1014
				elog(NOTICE, "SPI_connect() failed in RI_FKey_cascade_del()");
1015

1016
			/*
1017 1018 1019 1020 1021 1022
			 * Fetch or prepare a saved plan for the cascaded delete
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
1023
				char	   *querysep;
1024 1025 1026 1027
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
1028
				 *	DELETE FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]
1029 1030 1031 1032 1033 1034
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
1035
				sprintf(querystr, "DELETE FROM ONLY \"%s\"",
1036
						tgargs[RI_FK_RELNAME_ARGNO]);
1037 1038 1039
				querysep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
1040 1041
					sprintf(buf, " %s \"%s\" = $%d", querysep,
							tgargs[4 + i * 2], i + 1);
1042 1043 1044
					strcat(querystr, buf);
					querysep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
1045
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
1046 1047
				}

1048
				/*
1049 1050 1051 1052 1053 1054 1055
				 * Prepare, save and remember the new plan.
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

1056
			/*
1057 1058 1059 1060 1061 1062
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the deleted PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				del_values[i] = SPI_getbinval(old_row,
1063 1064 1065 1066
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
1067 1068 1069 1070
					del_nulls[i] = 'n';
				else
					del_nulls[i] = ' ';
			}
Jan Wieck's avatar
Jan Wieck committed
1071
			del_nulls[i] = '\0';
1072

1073
			/*
1074 1075
			 * Now delete constraint
			 */
1076 1077 1078
			save_uid = GetUserId();
			SetUserId(fk_owner);

Jan Wieck's avatar
Jan Wieck committed
1079
			if (SPI_execp(qplan, del_values, del_nulls, 0) != SPI_OK_DELETE)
1080
				elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_del()");
1081

1082 1083
			SetUserId(save_uid);

1084 1085 1086
			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_cascade_del()");

1087
			return PointerGetDatum(NULL);
1088

1089
			/*
1090 1091
			 * Handle MATCH PARTIAL cascaded delete.
			 */
1092 1093
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
1094
			return PointerGetDatum(NULL);
1095 1096
	}

1097
	/*
1098 1099
	 * Never reached
	 */
1100
	elog(ERROR, "internal error #4 in ri_triggers.c");
1101
	return PointerGetDatum(NULL);
1102 1103 1104 1105 1106 1107 1108 1109 1110
}


/* ----------
 * RI_FKey_cascade_upd -
 *
 *	Cascaded update/delete foreign key references at update event on PK table.
 * ----------
 */
1111 1112
Datum
RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
1113
{
1114
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS * 2];
	char		upd_nulls[RI_MAX_NUMKEYS * 2 + 1];
	bool		isnull;
	int			i;
	int			j;
1128 1129
	Oid			save_uid;
	Oid			fk_owner;
1130

1131
	ReferentialIntegritySnapshotOverride = true;
1132

1133 1134 1135
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
Jan Wieck's avatar
Jan Wieck committed
1136
	 */
1137
	if (!CALLED_AS_TRIGGER(fcinfo))
Jan Wieck's avatar
Jan Wieck committed
1138
		elog(ERROR, "RI_FKey_cascade_upd() not fired by trigger manager");
1139 1140
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
Jan Wieck's avatar
Jan Wieck committed
1141 1142 1143 1144
		elog(ERROR, "RI_FKey_cascade_upd() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_cascade_upd() must be fired for UPDATE");

1145
	/*
1146
	 * Check for the correct # of call arguments
Jan Wieck's avatar
Jan Wieck committed
1147 1148
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
1149
	tgargs = trigdata->tg_trigger->tgargs;
Jan Wieck's avatar
Jan Wieck committed
1150 1151 1152 1153
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()",
1154
			 RI_MAX_NUMKEYS);
Jan Wieck's avatar
Jan Wieck committed
1155

1156
	/*
Jan Wieck's avatar
Jan Wieck committed
1157 1158 1159
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
1160
		return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1161

1162 1163 1164
	/*
	 * Get the relation descriptors of the FK and PK tables and the new
	 * and old tuple.
Jan Wieck's avatar
Jan Wieck committed
1165
	 */
1166
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
1167
	fk_owner = RelationGetForm(fk_rel)->relowner;
1168
	pk_rel = trigdata->tg_relation;
Jan Wieck's avatar
Jan Wieck committed
1169 1170 1171 1172 1173
	new_row = trigdata->tg_newtuple;
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
1174 1175 1176 1177 1178 1179 1180
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 7) a) i):
			 *		MATCH <unspecified> or MATCH FULL
			 *			... ON UPDATE CASCADE
			 * ----------
			 */
Jan Wieck's avatar
Jan Wieck committed
1181 1182 1183
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
1184 1185 1186
								 RI_PLAN_CASCADE_UPD_DOUPDATE,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
Jan Wieck's avatar
Jan Wieck committed
1187 1188 1189 1190 1191

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
1192 1193

					/*
Jan Wieck's avatar
Jan Wieck committed
1194 1195 1196 1197
					 * No update - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
1198
					return PointerGetDatum(NULL);
1199

Jan Wieck's avatar
Jan Wieck committed
1200
				case RI_KEYS_NONE_NULL:
1201 1202

					/*
Jan Wieck's avatar
Jan Wieck committed
1203 1204 1205 1206 1207 1208
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

1209
			/*
Jan Wieck's avatar
Jan Wieck committed
1210 1211 1212
			 * No need to do anything if old and new keys are equal
			 */
			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
1213
							 RI_KEYPAIR_PK_IDX))
1214
				return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1215 1216

			if (SPI_connect() != SPI_OK_CONNECT)
Jan Wieck's avatar
Jan Wieck committed
1217
				elog(NOTICE, "SPI_connect() failed in RI_FKey_cascade_upd()");
Jan Wieck's avatar
Jan Wieck committed
1218

1219 1220 1221
			/*
			 * Fetch or prepare a saved plan for the cascaded update of
			 * foreign references
Jan Wieck's avatar
Jan Wieck committed
1222 1223 1224 1225 1226 1227
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
				char		qualstr[8192];
1228 1229
				char	   *querysep;
				char	   *qualsep;
Jan Wieck's avatar
Jan Wieck committed
1230 1231 1232 1233
				Oid			queryoids[RI_MAX_NUMKEYS * 2];

				/* ----------
				 * The query string built is
1234
				 *	UPDATE ONLY <fktable> SET fkatt1 = $1 [, ...]
Jan Wieck's avatar
Jan Wieck committed
1235 1236 1237 1238 1239 1240 1241
				 *			WHERE fkatt1 = $n [AND ...]
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
1242
				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
1243
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1244 1245 1246 1247 1248
				qualstr[0] = '\0';
				querysep = "";
				qualsep = "WHERE";
				for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)
				{
1249 1250
					sprintf(buf, "%s \"%s\" = $%d", querysep,
							tgargs[4 + i * 2], i + 1);
Jan Wieck's avatar
Jan Wieck committed
1251 1252
					strcat(querystr, buf);
					sprintf(buf, " %s \"%s\" = $%d", qualsep,
1253
							tgargs[4 + i * 2], j + 1);
Jan Wieck's avatar
Jan Wieck committed
1254 1255 1256 1257
					strcat(qualstr, buf);
					querysep = ",";
					qualsep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
1258
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
Jan Wieck's avatar
Jan Wieck committed
1259 1260 1261 1262
					queryoids[j] = queryoids[i];
				}
				strcat(querystr, qualstr);

1263
				/*
Jan Wieck's avatar
Jan Wieck committed
1264 1265 1266 1267 1268 1269 1270
				 * Prepare, save and remember the new plan.
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids);
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

1271
			/*
Jan Wieck's avatar
Jan Wieck committed
1272 1273 1274 1275 1276 1277
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the updated PK tuple.
			 */
			for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)
			{
				upd_values[i] = SPI_getbinval(new_row,
1278 1279 1280 1281
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
Jan Wieck's avatar
Jan Wieck committed
1282 1283 1284 1285 1286
					upd_nulls[i] = 'n';
				else
					upd_nulls[i] = ' ';

				upd_values[j] = SPI_getbinval(old_row,
1287 1288 1289 1290
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
Jan Wieck's avatar
Jan Wieck committed
1291 1292 1293 1294 1295 1296
					upd_nulls[j] = 'n';
				else
					upd_nulls[j] = ' ';
			}
			upd_nulls[j] = '\0';

1297
			/*
Jan Wieck's avatar
Jan Wieck committed
1298 1299
			 * Now update the existing references
			 */
1300 1301 1302
			save_uid = GetUserId();
			SetUserId(fk_owner);

Jan Wieck's avatar
Jan Wieck committed
1303 1304
			if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
				elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_upd()");
1305

1306 1307
			SetUserId(save_uid);

Jan Wieck's avatar
Jan Wieck committed
1308 1309 1310
			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_cascade_upd()");

1311
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1312

1313
			/*
1314 1315
			 * Handle MATCH PARTIAL cascade update.
			 */
Jan Wieck's avatar
Jan Wieck committed
1316 1317
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
1318
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1319 1320
	}

1321
	/*
Jan Wieck's avatar
Jan Wieck committed
1322 1323
	 * Never reached
	 */
1324
	elog(ERROR, "internal error #5 in ri_triggers.c");
1325
	return PointerGetDatum(NULL);
1326 1327 1328 1329 1330 1331 1332
}


/* ----------
 * RI_FKey_restrict_del -
 *
 *	Restrict delete from PK table to rows unreferenced by foreign key.
1333
 *
1334 1335 1336
 *	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".
1337
 *
1338 1339
 *	For now, however, we treat "RESTRICT" and "NO ACTION" as
 *	equivalent.
1340 1341
 * ----------
 */
1342 1343
Datum
RI_FKey_restrict_del(PG_FUNCTION_ARGS)
1344
{
1345
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		del_values[RI_MAX_NUMKEYS];
	char		del_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
1357 1358
	Oid			save_uid;
	Oid			fk_owner;
1359

1360
	ReferentialIntegritySnapshotOverride = true;
1361

1362 1363 1364
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
Jan Wieck's avatar
Jan Wieck committed
1365
	 */
1366
	if (!CALLED_AS_TRIGGER(fcinfo))
Jan Wieck's avatar
Jan Wieck committed
1367
		elog(ERROR, "RI_FKey_restrict_del() not fired by trigger manager");
1368 1369
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
Jan Wieck's avatar
Jan Wieck committed
1370 1371 1372 1373
		elog(ERROR, "RI_FKey_restrict_del() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_restrict_del() must be fired for DELETE");

1374
	/*
1375
	 * Check for the correct # of call arguments
Jan Wieck's avatar
Jan Wieck committed
1376 1377
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
1378
	tgargs = trigdata->tg_trigger->tgargs;
Jan Wieck's avatar
Jan Wieck committed
1379 1380 1381 1382
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()",
1383
			 RI_MAX_NUMKEYS);
Jan Wieck's avatar
Jan Wieck committed
1384

1385
	/*
Jan Wieck's avatar
Jan Wieck committed
1386 1387 1388
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
1389
		return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1390

1391 1392 1393
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
Jan Wieck's avatar
Jan Wieck committed
1394
	 */
1395
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
1396
	fk_owner = RelationGetForm(fk_rel)->relowner;
1397
	pk_rel = trigdata->tg_relation;
Jan Wieck's avatar
Jan Wieck committed
1398 1399 1400 1401
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
1402 1403 1404 1405 1406 1407 1408
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) iv):
			 *		MATCH <unspecified> or MATCH FULL
			 *			... ON DELETE CASCADE
			 * ----------
			 */
Jan Wieck's avatar
Jan Wieck committed
1409 1410 1411
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
1412 1413 1414
								 RI_PLAN_RESTRICT_DEL_CHECKREF,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
Jan Wieck's avatar
Jan Wieck committed
1415 1416 1417 1418 1419

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
1420 1421

					/*
Jan Wieck's avatar
Jan Wieck committed
1422 1423 1424 1425
					 * No check - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
1426
					return PointerGetDatum(NULL);
1427

Jan Wieck's avatar
Jan Wieck committed
1428
				case RI_KEYS_NONE_NULL:
1429 1430

					/*
Jan Wieck's avatar
Jan Wieck committed
1431 1432 1433 1434 1435 1436 1437 1438 1439
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_restrict_del()");

1440
			/*
Jan Wieck's avatar
Jan Wieck committed
1441
			 * Fetch or prepare a saved plan for the restrict delete
Jan Wieck's avatar
Jan Wieck committed
1442
			 * lookup if foreign references exist
Jan Wieck's avatar
Jan Wieck committed
1443 1444 1445 1446 1447
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
1448
				char	   *querysep;
Jan Wieck's avatar
Jan Wieck committed
1449 1450 1451 1452
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
1453
				 *	SELECT oid FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]
Jan Wieck's avatar
Jan Wieck committed
1454 1455 1456 1457 1458 1459
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
1460
				sprintf(querystr, "SELECT oid FROM ONLY \"%s\"",
1461
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1462 1463 1464
				querysep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
1465 1466
					sprintf(buf, " %s \"%s\" = $%d", querysep,
							tgargs[4 + i * 2], i + 1);
Jan Wieck's avatar
Jan Wieck committed
1467 1468 1469
					strcat(querystr, buf);
					querysep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
1470
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
Jan Wieck's avatar
Jan Wieck committed
1471
				}
1472 1473
				sprintf(buf, " FOR UPDATE OF \"%s\"",
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1474
				strcat(querystr, buf);
Jan Wieck's avatar
Jan Wieck committed
1475

1476
				/*
Jan Wieck's avatar
Jan Wieck committed
1477 1478 1479 1480 1481 1482 1483
				 * Prepare, save and remember the new plan.
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

1484
			/*
Jan Wieck's avatar
Jan Wieck committed
1485 1486 1487 1488 1489 1490
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the deleted PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				del_values[i] = SPI_getbinval(old_row,
1491 1492 1493 1494
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
Jan Wieck's avatar
Jan Wieck committed
1495 1496 1497 1498 1499 1500
					del_nulls[i] = 'n';
				else
					del_nulls[i] = ' ';
			}
			del_nulls[i] = '\0';

1501
			/*
Jan Wieck's avatar
Jan Wieck committed
1502 1503
			 * Now check for existing references
			 */
1504 1505 1506
			save_uid = GetUserId();
			SetUserId(fk_owner);

Jan Wieck's avatar
Jan Wieck committed
1507 1508
			if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
				elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");
1509

1510 1511
			SetUserId(save_uid);

Jan Wieck's avatar
Jan Wieck committed
1512 1513
			if (SPI_processed > 0)
				elog(ERROR, "%s referential integrity violation - "
1514 1515 1516 1517
					 "key in %s still referenced from %s",
					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
					 tgargs[RI_PK_RELNAME_ARGNO],
					 tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1518 1519 1520 1521

			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_restrict_del()");

1522
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1523

1524
			/*
1525 1526
			 * Handle MATCH PARTIAL restrict delete.
			 */
Jan Wieck's avatar
Jan Wieck committed
1527 1528
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
1529
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1530 1531
	}

1532
	/*
Jan Wieck's avatar
Jan Wieck committed
1533 1534
	 * Never reached
	 */
1535
	elog(ERROR, "internal error #6 in ri_triggers.c");
1536
	return PointerGetDatum(NULL);
1537 1538 1539 1540 1541 1542 1543
}


/* ----------
 * RI_FKey_restrict_upd -
 *
 *	Restrict update of PK to rows unreferenced by foreign key.
1544
 *
1545 1546 1547
 *	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".
1548
 *
1549 1550
 *	For now, however, we treat "RESTRICT" and "NO ACTION" as
 *	equivalent.
1551 1552
 * ----------
 */
1553 1554
Datum
RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
1555
{
1556
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS];
	char		upd_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
1569
	Oid			save_uid;
1570
	Oid			fk_owner;
1571

1572
	ReferentialIntegritySnapshotOverride = true;
1573

1574 1575 1576
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
Jan Wieck's avatar
Jan Wieck committed
1577
	 */
1578
	if (!CALLED_AS_TRIGGER(fcinfo))
Jan Wieck's avatar
Jan Wieck committed
1579
		elog(ERROR, "RI_FKey_restrict_upd() not fired by trigger manager");
1580 1581
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
Jan Wieck's avatar
Jan Wieck committed
1582 1583 1584 1585
		elog(ERROR, "RI_FKey_restrict_upd() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_restrict_upd() must be fired for UPDATE");

1586
	/*
1587
	 * Check for the correct # of call arguments
Jan Wieck's avatar
Jan Wieck committed
1588 1589
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
1590
	tgargs = trigdata->tg_trigger->tgargs;
Jan Wieck's avatar
Jan Wieck committed
1591 1592 1593 1594
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()",
1595
			 RI_MAX_NUMKEYS);
Jan Wieck's avatar
Jan Wieck committed
1596

1597
	/*
Jan Wieck's avatar
Jan Wieck committed
1598 1599 1600
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
1601
		return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1602

1603 1604 1605
	/*
	 * Get the relation descriptors of the FK and PK tables and the new
	 * and old tuple.
Jan Wieck's avatar
Jan Wieck committed
1606
	 */
1607
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
1608
	fk_owner = RelationGetForm(fk_rel)->relowner;
1609
	pk_rel = trigdata->tg_relation;
Jan Wieck's avatar
Jan Wieck committed
1610 1611 1612 1613 1614
	new_row = trigdata->tg_newtuple;
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
1615 1616 1617 1618 1619 1620 1621
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) iv):
			 *		MATCH <unspecified> or MATCH FULL
			 *			... ON DELETE CASCADE
			 * ----------
			 */
Jan Wieck's avatar
Jan Wieck committed
1622 1623 1624
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
1625 1626 1627
								 RI_PLAN_RESTRICT_UPD_CHECKREF,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
Jan Wieck's avatar
Jan Wieck committed
1628 1629 1630 1631 1632

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
1633 1634

					/*
Jan Wieck's avatar
Jan Wieck committed
1635 1636 1637 1638
					 * No check - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
1639
					return PointerGetDatum(NULL);
1640

Jan Wieck's avatar
Jan Wieck committed
1641
				case RI_KEYS_NONE_NULL:
1642 1643

					/*
Jan Wieck's avatar
Jan Wieck committed
1644 1645 1646 1647 1648 1649
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

1650
			/*
Jan Wieck's avatar
Jan Wieck committed
1651 1652 1653
			 * No need to check anything if old and new keys are equal
			 */
			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
1654
							 RI_KEYPAIR_PK_IDX))
1655
				return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1656 1657 1658 1659

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_restrict_upd()");

1660
			/*
Jan Wieck's avatar
Jan Wieck committed
1661 1662
			 * Fetch or prepare a saved plan for the restrict update
			 * lookup if foreign references exist
Jan Wieck's avatar
Jan Wieck committed
1663 1664 1665 1666 1667
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
1668
				char	   *querysep;
Jan Wieck's avatar
Jan Wieck committed
1669 1670 1671 1672
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
1673
				 *	SELECT oid FROM ONLY <fktable> WHERE fkatt1 = $1 [AND ...]
Jan Wieck's avatar
Jan Wieck committed
1674 1675 1676 1677 1678 1679
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
1680
				sprintf(querystr, "SELECT oid FROM ONLY \"%s\"",
1681
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1682 1683 1684
				querysep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
1685 1686
					sprintf(buf, " %s \"%s\" = $%d", querysep,
							tgargs[4 + i * 2], i + 1);
Jan Wieck's avatar
Jan Wieck committed
1687 1688 1689
					strcat(querystr, buf);
					querysep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
1690
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
Jan Wieck's avatar
Jan Wieck committed
1691
				}
1692 1693
				sprintf(buf, " FOR UPDATE OF \"%s\"",
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1694
				strcat(querystr, buf);
Jan Wieck's avatar
Jan Wieck committed
1695

1696
				/*
Jan Wieck's avatar
Jan Wieck committed
1697 1698 1699 1700 1701 1702 1703
				 * Prepare, save and remember the new plan.
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

1704
			/*
Jan Wieck's avatar
Jan Wieck committed
1705 1706 1707 1708 1709 1710
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the updated PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				upd_values[i] = SPI_getbinval(old_row,
1711 1712 1713 1714
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
Jan Wieck's avatar
Jan Wieck committed
1715 1716 1717 1718 1719 1720
					upd_nulls[i] = 'n';
				else
					upd_nulls[i] = ' ';
			}
			upd_nulls[i] = '\0';

1721
			/*
Jan Wieck's avatar
Jan Wieck committed
1722 1723
			 * Now check for existing references
			 */
1724 1725 1726
			save_uid = GetUserId();
			SetUserId(fk_owner);

1727 1728
			SetUserId(RelationGetForm(pk_rel)->relowner);

Jan Wieck's avatar
Jan Wieck committed
1729 1730
			if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
				elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");
1731

1732 1733
			SetUserId(save_uid);

Jan Wieck's avatar
Jan Wieck committed
1734 1735
			if (SPI_processed > 0)
				elog(ERROR, "%s referential integrity violation - "
1736 1737 1738 1739
					 "key in %s still referenced from %s",
					 tgargs[RI_CONSTRAINT_NAME_ARGNO],
					 tgargs[RI_PK_RELNAME_ARGNO],
					 tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1740 1741 1742 1743

			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_restrict_upd()");

1744
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1745

1746
			/*
1747 1748
			 * Handle MATCH PARTIAL restrict update.
			 */
Jan Wieck's avatar
Jan Wieck committed
1749 1750
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
1751
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1752 1753
	}

1754
	/*
Jan Wieck's avatar
Jan Wieck committed
1755 1756
	 * Never reached
	 */
1757
	elog(ERROR, "internal error #7 in ri_triggers.c");
1758
	return PointerGetDatum(NULL);
1759 1760 1761 1762 1763 1764 1765 1766 1767
}


/* ----------
 * RI_FKey_setnull_del -
 *
 *	Set foreign key references to NULL values at delete event on PK table.
 * ----------
 */
1768 1769
Datum
RI_FKey_setnull_del(PG_FUNCTION_ARGS)
1770
{
1771
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS];
	char		upd_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
1783 1784
	Oid			save_uid;
	Oid			fk_owner;
1785

1786
	ReferentialIntegritySnapshotOverride = true;
1787

1788 1789 1790
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
Jan Wieck's avatar
Jan Wieck committed
1791
	 */
1792
	if (!CALLED_AS_TRIGGER(fcinfo))
Jan Wieck's avatar
Jan Wieck committed
1793
		elog(ERROR, "RI_FKey_setnull_del() not fired by trigger manager");
1794 1795
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
Jan Wieck's avatar
Jan Wieck committed
1796 1797 1798 1799
		elog(ERROR, "RI_FKey_setnull_del() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_setnull_del() must be fired for DELETE");

1800
	/*
1801
	 * Check for the correct # of call arguments
Jan Wieck's avatar
Jan Wieck committed
1802 1803
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
1804
	tgargs = trigdata->tg_trigger->tgargs;
Jan Wieck's avatar
Jan Wieck committed
1805 1806 1807 1808
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()",
1809
			 RI_MAX_NUMKEYS);
Jan Wieck's avatar
Jan Wieck committed
1810

1811
	/*
Jan Wieck's avatar
Jan Wieck committed
1812 1813 1814
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
1815
		return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1816

1817 1818 1819
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
Jan Wieck's avatar
Jan Wieck committed
1820
	 */
1821
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
1822
	fk_owner = RelationGetForm(fk_rel)->relowner;
1823
	pk_rel = trigdata->tg_relation;
Jan Wieck's avatar
Jan Wieck committed
1824 1825 1826 1827
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
1828 1829 1830 1831 1832 1833 1834
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) ii):
			 *		MATCH <UNSPECIFIED> or MATCH FULL
			 *			... ON DELETE SET NULL
			 * ----------
			 */
Jan Wieck's avatar
Jan Wieck committed
1835 1836 1837
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
1838 1839 1840
								 RI_PLAN_SETNULL_DEL_DOUPDATE,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
Jan Wieck's avatar
Jan Wieck committed
1841 1842 1843 1844 1845

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
1846 1847

					/*
Jan Wieck's avatar
Jan Wieck committed
1848 1849 1850 1851
					 * No update - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
1852
					return PointerGetDatum(NULL);
1853

Jan Wieck's avatar
Jan Wieck committed
1854
				case RI_KEYS_NONE_NULL:
1855 1856

					/*
Jan Wieck's avatar
Jan Wieck committed
1857 1858 1859 1860 1861 1862 1863 1864 1865
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_setnull_del()");

1866
			/*
Jan Wieck's avatar
Jan Wieck committed
1867 1868 1869 1870 1871 1872 1873 1874
			 * Fetch or prepare a saved plan for the set null delete
			 * operation
			 */
			if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
			{
				char		buf[256];
				char		querystr[8192];
				char		qualstr[8192];
1875 1876
				char	   *querysep;
				char	   *qualsep;
Jan Wieck's avatar
Jan Wieck committed
1877 1878 1879 1880
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
1881
				 *	UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
Jan Wieck's avatar
Jan Wieck committed
1882 1883 1884 1885 1886 1887 1888
				 *			WHERE fkatt1 = $1 [AND ...]
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
1889
				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
1890
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
1891 1892 1893 1894 1895
				qualstr[0] = '\0';
				querysep = "";
				qualsep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
1896 1897
					sprintf(buf, "%s \"%s\" = NULL", querysep,
							tgargs[4 + i * 2]);
Jan Wieck's avatar
Jan Wieck committed
1898 1899
					strcat(querystr, buf);
					sprintf(buf, " %s \"%s\" = $%d", qualsep,
1900
							tgargs[4 + i * 2], i + 1);
Jan Wieck's avatar
Jan Wieck committed
1901 1902 1903 1904
					strcat(qualstr, buf);
					querysep = ",";
					qualsep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
1905
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
Jan Wieck's avatar
Jan Wieck committed
1906 1907 1908
				}
				strcat(querystr, qualstr);

1909
				/*
Jan Wieck's avatar
Jan Wieck committed
1910 1911
				 * Prepare, save and remember the new plan.
				 */
1912
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
Jan Wieck's avatar
Jan Wieck committed
1913 1914 1915 1916
				qplan = SPI_saveplan(qplan);
				ri_HashPreparedPlan(&qkey, qplan);
			}

1917
			/*
Jan Wieck's avatar
Jan Wieck committed
1918 1919 1920 1921 1922 1923
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the updated PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				upd_values[i] = SPI_getbinval(old_row,
1924 1925 1926 1927
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
Jan Wieck's avatar
Jan Wieck committed
1928 1929 1930 1931 1932 1933
					upd_nulls[i] = 'n';
				else
					upd_nulls[i] = ' ';
			}
			upd_nulls[i] = '\0';

1934
			/*
Jan Wieck's avatar
Jan Wieck committed
1935 1936
			 * Now update the existing references
			 */
1937 1938 1939
			save_uid = GetUserId();
			SetUserId(fk_owner);

Jan Wieck's avatar
Jan Wieck committed
1940 1941
			if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
				elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_del()");
1942

1943 1944
			SetUserId(save_uid);

Jan Wieck's avatar
Jan Wieck committed
1945 1946 1947
			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_setnull_del()");

1948
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1949

1950
			/*
1951 1952
			 * Handle MATCH PARTIAL set null delete.
			 */
Jan Wieck's avatar
Jan Wieck committed
1953 1954
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
1955
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
1956 1957
	}

1958
	/*
Jan Wieck's avatar
Jan Wieck committed
1959 1960
	 * Never reached
	 */
1961
	elog(ERROR, "internal error #8 in ri_triggers.c");
1962
	return PointerGetDatum(NULL);
1963 1964 1965 1966 1967 1968 1969 1970 1971
}


/* ----------
 * RI_FKey_setnull_upd -
 *
 *	Set foreign key references to NULL at update event on PK table.
 * ----------
 */
1972 1973
Datum
RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
1974
{
1975
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS];
	char		upd_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
	int			match_type;
	bool		use_cached_query;
1990 1991
	Oid			save_uid;
	Oid			fk_owner;
1992

1993
	ReferentialIntegritySnapshotOverride = true;
1994

1995 1996 1997
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
Jan Wieck's avatar
Jan Wieck committed
1998
	 */
1999
	if (!CALLED_AS_TRIGGER(fcinfo))
Jan Wieck's avatar
Jan Wieck committed
2000
		elog(ERROR, "RI_FKey_setnull_upd() not fired by trigger manager");
2001 2002
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
Jan Wieck's avatar
Jan Wieck committed
2003 2004 2005 2006
		elog(ERROR, "RI_FKey_setnull_upd() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_setnull_upd() must be fired for UPDATE");

2007
	/*
2008
	 * Check for the correct # of call arguments
Jan Wieck's avatar
Jan Wieck committed
2009 2010
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
2011
	tgargs = trigdata->tg_trigger->tgargs;
Jan Wieck's avatar
Jan Wieck committed
2012 2013 2014 2015
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()",
2016
			 RI_MAX_NUMKEYS);
Jan Wieck's avatar
Jan Wieck committed
2017

2018
	/*
Jan Wieck's avatar
Jan Wieck committed
2019 2020 2021
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
2022
		return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
2023

2024 2025 2026
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
Jan Wieck's avatar
Jan Wieck committed
2027
	 */
2028
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
2029
	fk_owner = RelationGetForm(fk_rel)->relowner;
2030
	pk_rel = trigdata->tg_relation;
Jan Wieck's avatar
Jan Wieck committed
2031 2032
	new_row = trigdata->tg_newtuple;
	old_row = trigdata->tg_trigtuple;
2033
	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
2034

2035
	switch (match_type)
Jan Wieck's avatar
Jan Wieck committed
2036
	{
2037 2038 2039 2040 2041 2042 2043
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 7) a) ii) 2):
			 *		MATCH FULL
			 *			... ON UPDATE SET NULL
			 * ----------
			 */
Jan Wieck's avatar
Jan Wieck committed
2044 2045 2046
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
2047 2048 2049
								 RI_PLAN_SETNULL_UPD_DOUPDATE,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
Jan Wieck's avatar
Jan Wieck committed
2050 2051 2052 2053 2054

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
2055 2056

					/*
Jan Wieck's avatar
Jan Wieck committed
2057 2058 2059 2060
					 * No update - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
2061
					return PointerGetDatum(NULL);
2062

Jan Wieck's avatar
Jan Wieck committed
2063
				case RI_KEYS_NONE_NULL:
2064 2065

					/*
Jan Wieck's avatar
Jan Wieck committed
2066 2067 2068 2069 2070 2071
					 * Have a full qualified key - continue below
					 */
					break;
			}
			heap_close(fk_rel, NoLock);

2072

2073
			/*
Jan Wieck's avatar
Jan Wieck committed
2074 2075 2076
			 * No need to do anything if old and new keys are equal
			 */
			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
2077
							 RI_KEYPAIR_PK_IDX))
2078
				return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
2079 2080 2081 2082

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_setnull_upd()");

2083 2084 2085 2086 2087 2088 2089 2090 2091
			/*
			 * "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.
2092 2093 2094 2095 2096 2097
			 *
			 * 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 = match_type == RI_MATCH_TYPE_FULL ||
2098 2099
				ri_AllKeysUnequal(pk_rel, old_row, new_row,
								  &qkey, RI_KEYPAIR_PK_IDX);
2100

2101
			/*
Jan Wieck's avatar
Jan Wieck committed
2102
			 * Fetch or prepare a saved plan for the set null update
2103
			 * operation if possible, or build a temporary plan if not.
Jan Wieck's avatar
Jan Wieck committed
2104
			 */
2105 2106
			if (!use_cached_query ||
				(qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
Jan Wieck's avatar
Jan Wieck committed
2107 2108 2109 2110
			{
				char		buf[256];
				char		querystr[8192];
				char		qualstr[8192];
2111 2112
				char	   *querysep;
				char	   *qualsep;
Jan Wieck's avatar
Jan Wieck committed
2113 2114 2115 2116
				Oid			queryoids[RI_MAX_NUMKEYS];

				/* ----------
				 * The query string built is
2117
				 *	UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
Jan Wieck's avatar
Jan Wieck committed
2118 2119 2120 2121 2122 2123 2124
				 *			WHERE fkatt1 = $1 [AND ...]
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
2125
				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
2126
						tgargs[RI_FK_RELNAME_ARGNO]);
Jan Wieck's avatar
Jan Wieck committed
2127 2128 2129 2130 2131
				qualstr[0] = '\0';
				querysep = "";
				qualsep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
2132 2133 2134
					/*
					 * MATCH <unspecified> - only change columns
					 * corresponding to changed columns in pk_rel's key
2135 2136
					 */
					if (match_type == RI_MATCH_TYPE_FULL ||
2137 2138
					  !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey,
									  RI_KEYPAIR_PK_IDX))
2139
					{
2140 2141
						sprintf(buf, "%s \"%s\" = NULL", querysep,
								tgargs[4 + i * 2]);
2142 2143 2144
						strcat(querystr, buf);
						querysep = ",";
					}
Jan Wieck's avatar
Jan Wieck committed
2145
					sprintf(buf, " %s \"%s\" = $%d", qualsep,
2146
							tgargs[4 + i * 2], i + 1);
Jan Wieck's avatar
Jan Wieck committed
2147 2148 2149
					strcat(qualstr, buf);
					qualsep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
2150
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
Jan Wieck's avatar
Jan Wieck committed
2151 2152 2153
				}
				strcat(querystr, qualstr);

2154
				/*
2155
				 * Prepare the new plan.
Jan Wieck's avatar
Jan Wieck committed
2156
				 */
2157
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
2158

2159 2160 2161
				/*
				 * Save and remember the plan if we're building the
				 * "standard" plan.
2162 2163 2164 2165 2166 2167
				 */
				if (use_cached_query)
				{
					qplan = SPI_saveplan(qplan);
					ri_HashPreparedPlan(&qkey, qplan);
				}
Jan Wieck's avatar
Jan Wieck committed
2168 2169
			}

2170
			/*
Jan Wieck's avatar
Jan Wieck committed
2171 2172 2173 2174 2175 2176
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the updated PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				upd_values[i] = SPI_getbinval(old_row,
2177 2178 2179 2180
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
Jan Wieck's avatar
Jan Wieck committed
2181 2182 2183 2184 2185 2186
					upd_nulls[i] = 'n';
				else
					upd_nulls[i] = ' ';
			}
			upd_nulls[i] = '\0';

2187
			/*
Jan Wieck's avatar
Jan Wieck committed
2188 2189
			 * Now update the existing references
			 */
2190 2191 2192
			save_uid = GetUserId();
			SetUserId(fk_owner);

Jan Wieck's avatar
Jan Wieck committed
2193 2194
			if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
				elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_upd()");
2195

2196 2197
			SetUserId(save_uid);

Jan Wieck's avatar
Jan Wieck committed
2198 2199 2200
			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_setnull_upd()");

2201
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
2202

2203
			/*
2204 2205
			 * Handle MATCH PARTIAL set null update.
			 */
Jan Wieck's avatar
Jan Wieck committed
2206 2207
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
2208
			return PointerGetDatum(NULL);
Jan Wieck's avatar
Jan Wieck committed
2209 2210
	}

2211
	/*
Jan Wieck's avatar
Jan Wieck committed
2212 2213
	 * Never reached
	 */
2214
	elog(ERROR, "internal error #9 in ri_triggers.c");
2215
	return PointerGetDatum(NULL);
2216 2217 2218 2219 2220 2221 2222 2223 2224
}


/* ----------
 * RI_FKey_setdefault_del -
 *
 *	Set foreign key references to defaults at delete event on PK table.
 * ----------
 */
2225 2226
Datum
RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
2227
{
2228
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS];
	char		upd_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
2240 2241
	Oid			save_uid;
	Oid			fk_owner;
2242

2243
	ReferentialIntegritySnapshotOverride = true;
2244

2245 2246 2247
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
2248
	 */
2249
	if (!CALLED_AS_TRIGGER(fcinfo))
2250
		elog(ERROR, "RI_FKey_setdefault_del() not fired by trigger manager");
2251 2252
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
2253 2254 2255 2256
		elog(ERROR, "RI_FKey_setdefault_del() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_setdefault_del() must be fired for DELETE");

2257
	/*
2258
	 * Check for the correct # of call arguments
2259 2260
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
2261
	tgargs = trigdata->tg_trigger->tgargs;
2262 2263 2264 2265
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()",
2266
			 RI_MAX_NUMKEYS);
2267

2268
	/*
2269 2270 2271
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
2272
		return PointerGetDatum(NULL);
2273

2274 2275 2276
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
2277
	 */
2278
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
2279
	fk_owner = RelationGetForm(fk_rel)->relowner;
2280
	pk_rel = trigdata->tg_relation;
2281 2282 2283 2284
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
2285 2286 2287 2288 2289 2290 2291
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 6) a) iii):
			 *		MATCH <UNSPECIFIED> or MATCH FULL
			 *			... ON DELETE SET DEFAULT
			 * ----------
			 */
2292 2293 2294
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
2295 2296 2297
								 RI_PLAN_SETNULL_DEL_DOUPDATE,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
2298 2299 2300 2301 2302

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
2303 2304

					/*
2305 2306 2307 2308
					 * No update - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
2309
					return PointerGetDatum(NULL);
2310

2311
				case RI_KEYS_NONE_NULL:
2312 2313

					/*
2314 2315 2316 2317 2318 2319 2320 2321
					 * Have a full qualified key - continue below
					 */
					break;
			}

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_setdefault_del()");

2322
			/*
2323
			 * Prepare a plan for the set defalt delete operation.
2324 2325
			 * Unfortunately we need to do it on every invocation because
			 * the default value could potentially change between calls.
2326 2327 2328 2329 2330
			 */
			{
				char		buf[256];
				char		querystr[8192];
				char		qualstr[8192];
2331 2332
				char	   *querysep;
				char	   *qualsep;
2333
				Oid			queryoids[RI_MAX_NUMKEYS];
2334 2335 2336 2337 2338
				Plan	   *spi_plan;
				AttrDefault *defval;
				TargetEntry *spi_qptle;
				int			i,
							j;
2339 2340 2341

				/* ----------
				 * The query string built is
2342
				 *	UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
2343 2344 2345 2346 2347 2348 2349
				 *			WHERE fkatt1 = $1 [AND ...]
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
2350
				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
2351
						tgargs[RI_FK_RELNAME_ARGNO]);
2352 2353 2354 2355 2356
				qualstr[0] = '\0';
				querysep = "";
				qualsep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
2357 2358
					sprintf(buf, "%s \"%s\" = NULL", querysep,
							tgargs[4 + i * 2]);
2359 2360
					strcat(querystr, buf);
					sprintf(buf, " %s \"%s\" = $%d", qualsep,
2361
							tgargs[4 + i * 2], i + 1);
2362 2363 2364 2365
					strcat(qualstr, buf);
					querysep = ",";
					qualsep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
2366
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
2367 2368 2369
				}
				strcat(querystr, qualstr);

2370
				/*
2371 2372 2373 2374 2375
				 * Prepare the plan
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);

				/* ----------
2376 2377 2378 2379 2380 2381
				 * Here now follows very ugly code depending on internals
				 * of the SPI manager.
				 *
				 * EVIL EVIL EVIL (but must be - Jan)
				 *
				 * We replace the CONST NULL targetlist expressions
2382 2383 2384 2385
				 * in the generated plan by (any) default values found
				 * in the tuple constructor.
				 * ----------
				 */
2386
				spi_plan = (Plan *) lfirst(((_SPI_plan *) qplan)->ptlist);
2387 2388 2389 2390 2391 2392
				if (fk_rel->rd_att->constr != NULL)
					defval = fk_rel->rd_att->constr->defval;
				else
					defval = NULL;
				for (i = 0; i < qkey.nkeypairs && defval != NULL; i++)
				{
2393
					/*
2394 2395 2396 2397 2398
					 * For each key attribute lookup the tuple constructor
					 * for a corresponding default value
					 */
					for (j = 0; j < fk_rel->rd_att->constr->num_defval; j++)
					{
2399 2400
						if (defval[j].adnum ==
							qkey.keypair[i][RI_KEYPAIR_FK_IDX])
2401
						{
2402 2403 2404
							/*
							 * That's the one - push the expression from
							 * defval.adbin into the plan's targetlist
2405 2406
							 */
							spi_qptle = (TargetEntry *)
2407 2408
								nth(defval[j].adnum - 1,
									spi_plan->targetlist);
2409 2410 2411 2412 2413 2414 2415
							spi_qptle->expr = stringToNode(defval[j].adbin);

							break;
						}
					}
				}
			}
2416 2417
			/* fk_rel is no longer needed OK ? */
			heap_close(fk_rel, NoLock);
2418

2419
			/*
2420 2421 2422 2423 2424 2425
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the deleted PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				upd_values[i] = SPI_getbinval(old_row,
2426 2427 2428 2429
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
2430 2431 2432 2433 2434 2435
					upd_nulls[i] = 'n';
				else
					upd_nulls[i] = ' ';
			}
			upd_nulls[i] = '\0';

2436
			/*
2437 2438
			 * Now update the existing references
			 */
2439 2440 2441
			save_uid = GetUserId();
			SetUserId(fk_owner);

2442 2443
			if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
				elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_del()");
2444

2445 2446
			SetUserId(save_uid);

2447 2448 2449
			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_setdefault_del()");

2450
			return PointerGetDatum(NULL);
2451

2452
			/*
2453 2454
			 * Handle MATCH PARTIAL set null delete.
			 */
2455 2456
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
2457
			return PointerGetDatum(NULL);
2458 2459
	}

2460
	/*
2461 2462
	 * Never reached
	 */
2463
	elog(ERROR, "internal error #10 in ri_triggers.c");
2464
	return PointerGetDatum(NULL);
2465 2466 2467 2468 2469 2470 2471 2472 2473
}


/* ----------
 * RI_FKey_setdefault_upd -
 *
 *	Set foreign key references to defaults at update event on PK table.
 * ----------
 */
2474 2475
Datum
RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
2476
{
2477
	TriggerData *trigdata = (TriggerData *) fcinfo->context;
2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
	void	   *qplan;
	Datum		upd_values[RI_MAX_NUMKEYS];
	char		upd_nulls[RI_MAX_NUMKEYS + 1];
	bool		isnull;
	int			i;
	int			match_type;
2491 2492
	Oid			save_uid;
	Oid			fk_owner;
2493

2494
	ReferentialIntegritySnapshotOverride = true;
2495

2496 2497 2498
	/*
	 * Check that this is a valid trigger call on the right time and
	 * event.
2499
	 */
2500
	if (!CALLED_AS_TRIGGER(fcinfo))
2501
		elog(ERROR, "RI_FKey_setdefault_upd() not fired by trigger manager");
2502 2503
	if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
		!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
2504 2505 2506 2507
		elog(ERROR, "RI_FKey_setdefault_upd() must be fired AFTER ROW");
	if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
		elog(ERROR, "RI_FKey_setdefault_upd() must be fired for UPDATE");

2508
	/*
2509
	 * Check for the correct # of call arguments
2510 2511
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
2512
	tgargs = trigdata->tg_trigger->tgargs;
2513 2514 2515 2516
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()",
2517
			 RI_MAX_NUMKEYS);
2518

2519
	/*
2520 2521 2522
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
2523
		return PointerGetDatum(NULL);
2524

2525 2526 2527
	/*
	 * Get the relation descriptors of the FK and PK tables and the old
	 * tuple.
2528
	 */
2529
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
2530
	fk_owner = RelationGetForm(fk_rel)->relowner;
2531
	pk_rel = trigdata->tg_relation;
2532 2533 2534
	new_row = trigdata->tg_newtuple;
	old_row = trigdata->tg_trigtuple;

2535 2536 2537
	match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);

	switch (match_type)
2538
	{
2539 2540 2541 2542 2543 2544 2545
			/* ----------
			 * SQL3 11.9 <referential constraint definition>
			 *	Gereral rules 7) a) iii):
			 *		MATCH <UNSPECIFIED> or MATCH FULL
			 *			... ON UPDATE SET DEFAULT
			 * ----------
			 */
2546 2547 2548
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
2549 2550 2551
								 RI_PLAN_SETNULL_DEL_DOUPDATE,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
2552 2553 2554 2555 2556

			switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
			{
				case RI_KEYS_ALL_NULL:
				case RI_KEYS_SOME_NULL:
2557 2558

					/*
2559 2560 2561 2562
					 * No update - MATCH FULL means there cannot be any
					 * reference to old key if it contains NULL
					 */
					heap_close(fk_rel, NoLock);
2563
					return PointerGetDatum(NULL);
2564

2565
				case RI_KEYS_NONE_NULL:
2566 2567

					/*
2568 2569 2570 2571 2572
					 * Have a full qualified key - continue below
					 */
					break;
			}

2573
			/*
2574 2575 2576
			 * No need to do anything if old and new keys are equal
			 */
			if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
2577
							 RI_KEYPAIR_PK_IDX))
2578
				return PointerGetDatum(NULL);
2579 2580 2581 2582

			if (SPI_connect() != SPI_OK_CONNECT)
				elog(NOTICE, "SPI_connect() failed in RI_FKey_setdefault_upd()");

2583
			/*
2584
			 * Prepare a plan for the set defalt delete operation.
2585 2586
			 * Unfortunately we need to do it on every invocation because
			 * the default value could potentially change between calls.
2587 2588 2589 2590 2591
			 */
			{
				char		buf[256];
				char		querystr[8192];
				char		qualstr[8192];
2592 2593
				char	   *querysep;
				char	   *qualsep;
2594
				Oid			queryoids[RI_MAX_NUMKEYS];
2595 2596 2597 2598 2599
				Plan	   *spi_plan;
				AttrDefault *defval;
				TargetEntry *spi_qptle;
				int			i,
							j;
2600 2601 2602

				/* ----------
				 * The query string built is
2603
				 *	UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
2604 2605 2606 2607 2608 2609 2610
				 *			WHERE fkatt1 = $1 [AND ...]
				 * The type id's for the $ parameters are those of the
				 * corresponding PK attributes. Thus, SPI_prepare could
				 * eventually fail if the parser cannot identify some way
				 * how to compare these two types by '='.
				 * ----------
				 */
2611
				sprintf(querystr, "UPDATE ONLY \"%s\" SET",
2612
						tgargs[RI_FK_RELNAME_ARGNO]);
2613 2614 2615 2616 2617
				qualstr[0] = '\0';
				querysep = "";
				qualsep = "WHERE";
				for (i = 0; i < qkey.nkeypairs; i++)
				{
2618 2619 2620
					/*
					 * MATCH <unspecified> - only change columns
					 * corresponding to changed columns in pk_rel's key
2621 2622 2623
					 */
					if (match_type == RI_MATCH_TYPE_FULL ||
						!ri_OneKeyEqual(pk_rel, i, old_row,
2624
									  new_row, &qkey, RI_KEYPAIR_PK_IDX))
2625
					{
2626 2627
						sprintf(buf, "%s \"%s\" = NULL", querysep,
								tgargs[4 + i * 2]);
2628 2629 2630
						strcat(querystr, buf);
						querysep = ",";
					}
2631
					sprintf(buf, " %s \"%s\" = $%d", qualsep,
2632
							tgargs[4 + i * 2], i + 1);
2633 2634 2635
					strcat(qualstr, buf);
					qualsep = "AND";
					queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
2636
									 qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
2637 2638 2639
				}
				strcat(querystr, qualstr);

2640
				/*
2641 2642 2643 2644
				 * Prepare the plan
				 */
				qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);

2645 2646 2647 2648
				/*
				 * Now replace the CONST NULL targetlist expressions in
				 * the generated plan by (any) default values found in the
				 * tuple constructor.
2649
				 */
2650
				spi_plan = (Plan *) lfirst(((_SPI_plan *) qplan)->ptlist);
2651 2652 2653 2654 2655 2656
				if (fk_rel->rd_att->constr != NULL)
					defval = fk_rel->rd_att->constr->defval;
				else
					defval = NULL;
				for (i = 0; i < qkey.nkeypairs && defval != NULL; i++)
				{
2657 2658 2659 2660 2661
					/*
					 * MATCH <unspecified> - only change columns
					 * corresponding to changed columns in pk_rel's key.
					 * This conditional must match the one in the loop
					 * above that built the SET attrn=NULL list.
2662
					 */
2663 2664
					if (match_type == RI_MATCH_TYPE_FULL ||
						!ri_OneKeyEqual(pk_rel, i, old_row,
2665
									  new_row, &qkey, RI_KEYPAIR_PK_IDX))
2666
					{
2667 2668 2669
						/*
						 * For each key attribute lookup the tuple
						 * constructor for a corresponding default value
2670 2671
						 */
						for (j = 0; j < fk_rel->rd_att->constr->num_defval; j++)
2672
						{
2673 2674
							if (defval[j].adnum ==
								qkey.keypair[i][RI_KEYPAIR_FK_IDX])
2675
							{
2676
								/*
2677
								 * That's the one - push the expression
2678 2679
								 * from defval.adbin into the plan's
								 * targetlist
2680 2681
								 */
								spi_qptle = (TargetEntry *)
2682
									nth(defval[j].adnum - 1,
2683
										spi_plan->targetlist);
2684
								spi_qptle->expr = stringToNode(defval[j].adbin);
2685

2686
								break;
2687
							}
2688 2689 2690 2691
						}
					}
				}
			}
2692 2693
			/* fk_rel is no longer needed OK ? */
			heap_close(fk_rel, NoLock);
2694

2695
			/*
2696 2697 2698 2699 2700 2701
			 * We have a plan now. Build up the arguments for SPI_execp()
			 * from the key values in the deleted PK tuple.
			 */
			for (i = 0; i < qkey.nkeypairs; i++)
			{
				upd_values[i] = SPI_getbinval(old_row,
2702 2703 2704 2705
											  pk_rel->rd_att,
									  qkey.keypair[i][RI_KEYPAIR_PK_IDX],
											  &isnull);
				if (isnull)
2706 2707 2708 2709 2710 2711
					upd_nulls[i] = 'n';
				else
					upd_nulls[i] = ' ';
			}
			upd_nulls[i] = '\0';

2712
			/*
2713 2714
			 * Now update the existing references
			 */
2715 2716 2717
			save_uid = GetUserId();
			SetUserId(fk_owner);

2718 2719
			if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
				elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_upd()");
2720

2721 2722
			SetUserId(save_uid);

2723 2724 2725
			if (SPI_finish() != SPI_OK_FINISH)
				elog(NOTICE, "SPI_finish() failed in RI_FKey_setdefault_upd()");

2726
			return PointerGetDatum(NULL);
2727

2728
			/*
2729 2730
			 * Handle MATCH PARTIAL set null delete.
			 */
2731 2732
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
2733
			return PointerGetDatum(NULL);
2734 2735
	}

2736
	/*
2737 2738
	 * Never reached
	 */
2739
	elog(ERROR, "internal error #11 in ri_triggers.c");
2740
	return PointerGetDatum(NULL);
2741 2742 2743
}


2744 2745 2746 2747 2748
/* ----------
 * RI_FKey_keyequal_upd -
 *
 *	Check if we have a key change on update.
 *
2749
 *	This is not a real trigger procedure. It is used by the deferred
2750 2751 2752 2753
 *	trigger queue manager to detect "triggered data change violation".
 * ----------
 */
bool
2754
RI_FKey_keyequal_upd(TriggerData *trigdata)
2755
{
2756 2757 2758 2759 2760 2761 2762
	int			tgnargs;
	char	  **tgargs;
	Relation	fk_rel;
	Relation	pk_rel;
	HeapTuple	new_row;
	HeapTuple	old_row;
	RI_QueryKey qkey;
2763

2764
	/*
2765
	 * Check for the correct # of call arguments
2766 2767
	 */
	tgnargs = trigdata->tg_trigger->tgnargs;
2768
	tgargs = trigdata->tg_trigger->tgargs;
2769 2770 2771 2772
	if (tgnargs < 4 || (tgnargs % 2) != 0)
		elog(ERROR, "wrong # of arguments in call to RI_FKey_keyequal_upd()");
	if (tgnargs > RI_MAX_ARGUMENTS)
		elog(ERROR, "too many keys (%d max) in call to RI_FKey_keyequal_upd()",
2773
			 RI_MAX_NUMKEYS);
2774

2775
	/*
2776 2777 2778 2779 2780
	 * Nothing to do if no column names to compare given
	 */
	if (tgnargs == 4)
		return true;

2781 2782 2783
	/*
	 * Get the relation descriptors of the FK and PK tables and the new
	 * and old tuple.
2784
	 */
2785 2786
	fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock);
	pk_rel = trigdata->tg_relation;
2787 2788 2789 2790 2791
	new_row = trigdata->tg_newtuple;
	old_row = trigdata->tg_trigtuple;

	switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
	{
2792
			/*
2793 2794
			 * MATCH <UNSPECIFIED>
			 */
2795 2796 2797
		case RI_MATCH_TYPE_UNSPECIFIED:
		case RI_MATCH_TYPE_FULL:
			ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
2798 2799 2800
								 0,
								 fk_rel, pk_rel,
								 tgnargs, tgargs);
2801 2802
			heap_close(fk_rel, NoLock);

2803
			/*
2804 2805 2806
			 * Return if key's are equal
			 */
			return ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
2807
								RI_KEYPAIR_PK_IDX);
2808

2809
			/*
2810 2811
			 * Handle MATCH PARTIAL set null delete.
			 */
2812 2813 2814 2815 2816
		case RI_MATCH_TYPE_PARTIAL:
			elog(ERROR, "MATCH PARTIAL not yet supported");
			break;
	}

2817
	/*
2818 2819
	 * Never reached
	 */
2820
	elog(ERROR, "internal error #12 in ri_triggers.c");
2821 2822 2823 2824
	return false;
}


2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874



/* ----------
 * Local functions below
 * ----------
 */





/* ----------
 * ri_DetermineMatchType -
 *
 *	Convert the MATCH TYPE string into a switchable int
 * ----------
 */
static int
ri_DetermineMatchType(char *str)
{
	if (!strcmp(str, "UNSPECIFIED"))
		return RI_MATCH_TYPE_UNSPECIFIED;
	if (!strcmp(str, "FULL"))
		return RI_MATCH_TYPE_FULL;
	if (!strcmp(str, "PARTIAL"))
		return RI_MATCH_TYPE_PARTIAL;

	elog(ERROR, "unrecognized referential integrity MATCH type '%s'", str);
	return 0;
}


/* ----------
 * ri_BuildQueryKeyFull -
 *
 *	Build up a new hashtable key for a prepared SPI plan of a
 *	constraint trigger of MATCH FULL. The key consists of:
 *
 *		constr_type is FULL
 *		constr_id is the OID of the pg_trigger row that invoked us
 *		constr_queryno is an internal number of the query inside the proc
 *		fk_relid is the OID of referencing relation
 *		pk_relid is the OID of referenced relation
 *		nkeypairs is the number of keypairs
 *		following are the attribute number keypairs of the trigger invocation
 *
 *	At least for MATCH FULL this builds a unique key per plan.
 * ----------
 */
2875
static void
2876
ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
2877 2878
					 Relation fk_rel, Relation pk_rel,
					 int argc, char **argv)
2879
{
2880 2881 2882
	int			i;
	int			j;
	int			fno;
2883

2884
	/*
2885 2886
	 * Initialize the key and fill in type, oid's and number of keypairs
	 */
2887 2888 2889 2890 2891 2892 2893
	memset((void *) key, 0, sizeof(RI_QueryKey));
	key->constr_type = RI_MATCH_TYPE_FULL;
	key->constr_id = constr_id;
	key->constr_queryno = constr_queryno;
	key->fk_relid = fk_rel->rd_id;
	key->pk_relid = pk_rel->rd_id;
	key->nkeypairs = (argc - RI_FIRST_ATTNAME_ARGNO) / 2;
2894

2895
	/*
2896 2897 2898 2899 2900 2901 2902 2903
	 * Lookup the attribute numbers of the arguments to the trigger call
	 * and fill in the keypairs.
	 */
	for (i = 0, j = RI_FIRST_ATTNAME_ARGNO; j < argc; i++, j += 2)
	{
		fno = SPI_fnumber(fk_rel->rd_att, argv[j]);
		if (fno == SPI_ERROR_NOATTRIBUTE)
			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
2904 2905 2906
				 argv[RI_CONSTRAINT_NAME_ARGNO],
				 argv[RI_FK_RELNAME_ARGNO],
				 argv[j]);
2907 2908 2909 2910 2911
		key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;

		fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
		if (fno == SPI_ERROR_NOATTRIBUTE)
			elog(ERROR, "constraint %s: table %s does not have an attribute %s",
2912 2913 2914
				 argv[RI_CONSTRAINT_NAME_ARGNO],
				 argv[RI_PK_RELNAME_ARGNO],
				 argv[j + 1]);
2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929
		key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
	}

	return;
}


/* ----------
 * ri_NullCheck -
 *
 *	Determine the NULL state of all key values in a tuple
 *
 *	Returns one of RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL or RI_KEYS_SOME_NULL.
 * ----------
 */
2930
static int
2931 2932
ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
{
2933 2934 2935 2936
	int			i;
	bool		isnull;
	bool		allnull = true;
	bool		nonenull = true;
2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970

	for (i = 0; i < key->nkeypairs; i++)
	{
		isnull = false;
		SPI_getbinval(tup, rel->rd_att, key->keypair[i][pairidx], &isnull);
		if (isnull)
			nonenull = false;
		else
			allnull = false;
	}

	if (allnull)
		return RI_KEYS_ALL_NULL;

	if (nonenull)
		return RI_KEYS_NONE_NULL;

	return RI_KEYS_SOME_NULL;
}


/* ----------
 * ri_InitHashTables -
 *
 *	Initialize our internal hash tables for prepared
 *	query plans and equal operators.
 * ----------
 */
static void
ri_InitHashTables(void)
{
	HASHCTL		ctl;

	memset(&ctl, 0, sizeof(ctl));
2971
	ctl.keysize = sizeof(RI_QueryKey);
2972 2973
	ctl.entrysize = sizeof(RI_QueryHashEntry);
	ctl.hash = tag_hash;
2974 2975
	ri_query_cache = hash_create("RI query cache", RI_INIT_QUERYHASHSIZE,
								 &ctl, HASH_ELEM | HASH_FUNCTION);
2976

2977
	ctl.keysize = sizeof(Oid);
2978
	ctl.entrysize = sizeof(RI_OpreqHashEntry);
2979
	ctl.hash = tag_hash;
2980 2981
	ri_opreq_cache = hash_create("RI OpReq cache", RI_INIT_OPREQHASHSIZE,
								 &ctl, HASH_ELEM | HASH_FUNCTION);
2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994
}


/* ----------
 * ri_FetchPreparedPlan -
 *
 *	Lookup for a query key in our private hash table of prepared
 *	and saved SPI execution plans. Return the plan if found or NULL.
 * ----------
 */
static void *
ri_FetchPreparedPlan(RI_QueryKey *key)
{
2995
	RI_QueryHashEntry *entry;
2996

2997
	/*
2998 2999 3000 3001 3002
	 * On the first call initialize the hashtable
	 */
	if (!ri_query_cache)
		ri_InitHashTables();

3003
	/*
3004 3005
	 * Lookup for the key
	 */
3006
	entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
3007
											  (void *) key,
3008
											  HASH_FIND, NULL);
3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023
	if (entry == NULL)
		return NULL;
	return entry->plan;
}


/* ----------
 * ri_HashPreparedPlan -
 *
 *	Add another plan to our private SPI query plan hashtable.
 * ----------
 */
static void
ri_HashPreparedPlan(RI_QueryKey *key, void *plan)
{
3024 3025
	RI_QueryHashEntry *entry;
	bool		found;
3026

3027
	/*
3028 3029 3030 3031 3032
	 * On the first call initialize the hashtable
	 */
	if (!ri_query_cache)
		ri_InitHashTables();

3033
	/*
3034 3035
	 * Add the new plan.
	 */
3036
	entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
3037 3038
											  (void *) key,
											  HASH_ENTER, &found);
3039
	if (entry == NULL)
3040
		elog(ERROR, "out of memory for RI plan cache");
3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051
	entry->plan = plan;
}


/* ----------
 * ri_KeysEqual -
 *
 *	Check if all key values in OLD and NEW are equal.
 * ----------
 */
static bool
3052 3053
ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
			 RI_QueryKey *key, int pairidx)
3054
{
3055 3056 3057 3058 3059
	int			i;
	Oid			typeid;
	Datum		oldvalue;
	Datum		newvalue;
	bool		isnull;
3060 3061 3062

	for (i = 0; i < key->nkeypairs; i++)
	{
3063
		/*
3064 3065
		 * Get one attributes oldvalue. If it is NULL - they're not equal.
		 */
3066 3067
		oldvalue = SPI_getbinval(oldtup, rel->rd_att,
								 key->keypair[i][pairidx], &isnull);
3068 3069 3070
		if (isnull)
			return false;

3071
		/*
3072 3073
		 * Get one attributes oldvalue. If it is NULL - they're not equal.
		 */
3074 3075
		newvalue = SPI_getbinval(newtup, rel->rd_att,
								 key->keypair[i][pairidx], &isnull);
3076 3077 3078
		if (isnull)
			return false;

3079 3080 3081
		/*
		 * Get the attributes type OID and call the '=' operator to
		 * compare the values.
3082 3083 3084 3085 3086 3087 3088 3089 3090 3091
		 */
		typeid = SPI_gettypeid(rel->rd_att, key->keypair[i][pairidx]);
		if (!ri_AttributesEqual(typeid, oldvalue, newvalue))
			return false;
	}

	return true;
}


3092 3093 3094 3095 3096 3097 3098
/* ----------
 * ri_AllKeysUnequal -
 *
 *	Check if all key values in OLD and NEW are not equal.
 * ----------
 */
static bool
3099 3100
ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
				  RI_QueryKey *key, int pairidx)
3101
{
3102 3103 3104 3105 3106 3107
	int			i;
	Oid			typeid;
	Datum		oldvalue;
	Datum		newvalue;
	bool		isnull;
	bool		keys_unequal;
3108 3109 3110 3111

	keys_unequal = true;
	for (i = 0; keys_unequal && i < key->nkeypairs; i++)
	{
3112
		/*
3113 3114
		 * Get one attributes oldvalue. If it is NULL - they're not equal.
		 */
3115 3116
		oldvalue = SPI_getbinval(oldtup, rel->rd_att,
								 key->keypair[i][pairidx], &isnull);
3117 3118 3119
		if (isnull)
			continue;

3120
		/*
3121 3122
		 * Get one attributes oldvalue. If it is NULL - they're not equal.
		 */
3123 3124
		newvalue = SPI_getbinval(newtup, rel->rd_att,
								 key->keypair[i][pairidx], &isnull);
3125 3126 3127
		if (isnull)
			continue;

3128 3129 3130
		/*
		 * Get the attributes type OID and call the '=' operator to
		 * compare the values.
3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146
		 */
		typeid = SPI_gettypeid(rel->rd_att, key->keypair[i][pairidx]);
		if (!ri_AttributesEqual(typeid, oldvalue, newvalue))
			continue;
		keys_unequal = false;
	}

	return keys_unequal;
}


/* ----------
 * ri_OneKeyEqual -
 *
 *	Check if one key value in OLD and NEW is equal.
 *
3147 3148
 *	ri_KeysEqual could call this but would run a bit slower.  For
 *	now, let's duplicate the code.
3149 3150 3151
 * ----------
 */
static bool
3152 3153
ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
			   RI_QueryKey *key, int pairidx)
3154
{
3155 3156 3157 3158
	Oid			typeid;
	Datum		oldvalue;
	Datum		newvalue;
	bool		isnull;
3159

3160
	/*
3161 3162
	 * Get one attributes oldvalue. If it is NULL - they're not equal.
	 */
3163 3164
	oldvalue = SPI_getbinval(oldtup, rel->rd_att,
							 key->keypair[column][pairidx], &isnull);
3165 3166 3167
	if (isnull)
		return false;

3168
	/*
3169 3170
	 * Get one attributes oldvalue. If it is NULL - they're not equal.
	 */
3171 3172
	newvalue = SPI_getbinval(newtup, rel->rd_att,
							 key->keypair[column][pairidx], &isnull);
3173 3174 3175
	if (isnull)
		return false;

3176 3177 3178
	/*
	 * Get the attributes type OID and call the '=' operator to compare
	 * the values.
3179 3180 3181
	 */
	typeid = SPI_gettypeid(rel->rd_att, key->keypair[column][pairidx]);
	if (!ri_AttributesEqual(typeid, oldvalue, newvalue))
3182
		return false;
3183 3184 3185 3186 3187

	return true;
}


3188 3189 3190
/* ----------
 * ri_AttributesEqual -
 *
3191
 *	Call the type specific '=' operator comparison function
3192
 *	for two values.
3193 3194
 *
 *	NB: we have already checked that neither value is null.
3195 3196 3197 3198 3199
 * ----------
 */
static bool
ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
{
3200 3201
	RI_OpreqHashEntry *entry;
	bool		found;
3202

3203
	/*
3204 3205
	 * On the first call initialize the hashtable
	 */
3206
	if (!ri_opreq_cache)
3207 3208
		ri_InitHashTables();

3209
	/*
3210 3211
	 * Try to find the '=' operator for this type in our cache
	 */
3212
	entry = (RI_OpreqHashEntry *) hash_search(ri_opreq_cache,
3213
											  (void *) &typeid,
3214
											  HASH_FIND, NULL);
3215

3216 3217 3218 3219
	/*
	 * If not found, lookup the OPERNAME system cache for it to get the
	 * func OID, then do the function manager lookup, and remember that
	 * info.
3220
	 */
3221
	if (!entry)
3222
	{
3223
		HeapTuple	opr_tup;
3224 3225
		Oid			opr_proc;
		FmgrInfo	finfo;
3226

3227 3228 3229 3230 3231
		opr_tup = SearchSysCache(OPERNAME,
								 PointerGetDatum("="),
								 ObjectIdGetDatum(typeid),
								 ObjectIdGetDatum(typeid),
								 CharGetDatum('b'));
3232
		if (!HeapTupleIsValid(opr_tup))
3233
			elog(ERROR,
3234
			"ri_AttributesEqual(): cannot find '=' operator for type %u",
3235 3236 3237 3238 3239 3240
				 typeid);
		opr_proc = ((Form_pg_operator) GETSTRUCT(opr_tup))->oprcode;
		ReleaseSysCache(opr_tup);

		/*
		 * Since fmgr_info could fail, call it *before* creating the
3241 3242 3243 3244
		 * hashtable entry --- otherwise we could elog leaving an
		 * incomplete entry in the hashtable.  Also, because this will be
		 * a permanent table entry, we must make sure any subsidiary
		 * structures of the fmgr record are kept in TopMemoryContext.
3245
		 */
3246
		fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);
3247

3248
		entry = (RI_OpreqHashEntry *) hash_search(ri_opreq_cache,
3249 3250
												  (void *) &typeid,
												  HASH_ENTER, &found);
3251
		if (entry == NULL)
3252
			elog(ERROR, "out of memory for RI operator cache");
3253

3254
		entry->typeid = typeid;
3255
		memcpy(&(entry->oprfmgrinfo), &finfo, sizeof(FmgrInfo));
3256 3257
	}

3258
	/*
3259 3260
	 * Call the type specific '=' function
	 */
3261 3262
	return DatumGetBool(FunctionCall2(&(entry->oprfmgrinfo),
									  oldvalue, newvalue));
3263
}