int32 constr_queryno);
static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
const RI_ConstraintInfo *riinfo, bool rel_is_pk);
-static bool ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
- const RI_ConstraintInfo *riinfo, bool rel_is_pk);
-static bool ri_OneKeyEqual(Relation rel, int column,
- HeapTuple oldtup, HeapTuple newtup,
- const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum oldvalue, Datum newvalue);
static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
RI_QueryKey qkey;
SPIPlanPtr qplan;
int i;
- bool use_cached_query;
/*
* Check that this is a valid trigger call on the right time and event.
/* ----------
* SQL3 11.9
* General rules 7) a) ii) 2):
- * MATCH FULL
+ * MATCH SIMPLE/FULL
* ... ON UPDATE SET NULL
* ----------
*/
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
- /*
- * "MATCH SIMPLE" only changes columns corresponding to the
- * referenced columns that have changed in pk_rel. This means the
- * "SET attrn=NULL [, attrn=NULL]" string will be change as well.
- * In this case, we need to build a temporary plan rather than use
- * our cached plan, unless the update happens to change all
- * columns in the key. Fortunately, for the most common case of a
- * single-column foreign key, this will be true.
- *
- * In case you're wondering, the inequality check works because we
- * know that the old key value has no NULLs (see above).
- */
-
- use_cached_query = (riinfo.confmatchtype == FKCONSTR_MATCH_FULL) ||
- ri_AllKeysUnequal(pk_rel, old_row, new_row,
- &riinfo, true);
-
/*
* Fetch or prepare a saved plan for the set null update operation
- * if possible, or build a temporary plan if not.
*/
- if (!use_cached_query ||
- (qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
+ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
StringInfoData qualbuf;
quoteOneName(attname,
RIAttName(fk_rel, riinfo.fk_attnums[i]));
-
- /*
- * MATCH SIMPLE - only change columns corresponding
- * to changed columns in pk_rel's key
- */
- if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
- !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
- &riinfo, true))
- {
- appendStringInfo(&querybuf,
- "%s %s = NULL",
- querysep, attname);
- querysep = ",";
- }
+ appendStringInfo(&querybuf,
+ "%s %s = NULL",
+ querysep, attname);
sprintf(paramname, "$%d", i + 1);
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo.pf_eq_oprs[i],
attname, fk_type);
+ querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
}
appendStringInfoString(&querybuf, qualbuf.data);
- /*
- * Prepare the plan. Save it only if we're building the
- * "standard" plan.
- */
+ /* Prepare and save the plan */
qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
- &qkey, fk_rel, pk_rel,
- use_cached_query);
+ &qkey, fk_rel, pk_rel, true);
}
/*
quoteOneName(attname,
RIAttName(fk_rel, riinfo.fk_attnums[i]));
-
- /*
- * MATCH SIMPLE - only change columns corresponding
- * to changed columns in pk_rel's key
- */
- if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
- !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
- &riinfo, true))
- {
- appendStringInfo(&querybuf,
- "%s %s = DEFAULT",
- querysep, attname);
- querysep = ",";
- }
+ appendStringInfo(&querybuf,
+ "%s %s = DEFAULT",
+ querysep, attname);
sprintf(paramname, "$%d", i + 1);
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo.pf_eq_oprs[i],
attname, fk_type);
+ querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
}
}
-/* ----------
- * ri_AllKeysUnequal -
- *
- * Check if all key values in OLD and NEW are not equal.
- * ----------
- */
-static bool
-ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
- const RI_ConstraintInfo *riinfo, bool rel_is_pk)
-{
- TupleDesc tupdesc = RelationGetDescr(rel);
- const int16 *attnums;
- const Oid *eq_oprs;
- int i;
-
- if (rel_is_pk)
- {
- attnums = riinfo->pk_attnums;
- eq_oprs = riinfo->pp_eq_oprs;
- }
- else
- {
- attnums = riinfo->fk_attnums;
- eq_oprs = riinfo->ff_eq_oprs;
- }
-
- for (i = 0; i < riinfo->nkeys; i++)
- {
- Datum oldvalue;
- Datum newvalue;
- bool isnull;
-
- /*
- * Get one attribute's oldvalue. If it is NULL - they're not equal.
- */
- oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
- if (isnull)
- continue;
-
- /*
- * Get one attribute's newvalue. If it is NULL - they're not equal.
- */
- newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
- if (isnull)
- continue;
-
- /*
- * Compare them with the appropriate equality operator.
- */
- if (ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
- oldvalue, newvalue))
- return false; /* found two equal items */
- }
-
- return true;
-}
-
-
-/* ----------
- * ri_OneKeyEqual -
- *
- * Check if one key value in OLD and NEW is equal. Note column is indexed
- * from zero.
- *
- * ri_KeysEqual could call this but would run a bit slower. For
- * now, let's duplicate the code.
- * ----------
- */
-static bool
-ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
- const RI_ConstraintInfo *riinfo, bool rel_is_pk)
-{
- TupleDesc tupdesc = RelationGetDescr(rel);
- const int16 *attnums;
- const Oid *eq_oprs;
- Datum oldvalue;
- Datum newvalue;
- bool isnull;
-
- if (rel_is_pk)
- {
- attnums = riinfo->pk_attnums;
- eq_oprs = riinfo->pp_eq_oprs;
- }
- else
- {
- attnums = riinfo->fk_attnums;
- eq_oprs = riinfo->ff_eq_oprs;
- }
-
- /*
- * Get one attribute's oldvalue. If it is NULL - they're not equal.
- */
- oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[column], &isnull);
- if (isnull)
- return false;
-
- /*
- * Get one attribute's newvalue. If it is NULL - they're not equal.
- */
- newvalue = SPI_getbinval(newtup, tupdesc, attnums[column], &isnull);
- if (isnull)
- return false;
-
- /*
- * Compare them with the appropriate equality operator.
- */
- if (!ri_AttributesEqual(eq_oprs[column], RIAttType(rel, attnums[column]),
- oldvalue, newvalue))
- return false;
-
- return true;
-}
-
/* ----------
* ri_AttributesEqual -
*
2 | | 3 | 3
| 2 | 7 | 4
| 3 | 4 | 5
- 1 | | 3 | 1
+ | | | 1
(6 rows)
-- Try to delete something that should set default
2 | | 3 | 3
| 2 | 7 | 4
| 3 | 4 | 5
- 1 | | 3 | 1
+ | | | 1
0 | | | 1
(6 rows)
2 | | 3 | 3
| 2 | 7 | 4
| 3 | 4 | 5
- 1 | | 3 | 1
+ | | | 1
0 | | | 1
(6 rows)
-- set default update / set null delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int DEFAULT -1, ftest3 int, ftest4 int, CONSTRAINT constrname3
+CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int DEFAULT -1, ftest3 int DEFAULT -2, ftest4 int, CONSTRAINT constrname3
FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE
ON DELETE SET NULL ON UPDATE SET DEFAULT);
-- Insert Primary Key values
-- Try to update something that will fail
UPDATE PKTABLE set ptest2=5 where ptest2=2;
ERROR: insert or update on table "fktable" violates foreign key constraint "constrname3"
-DETAIL: Key (ftest1, ftest2, ftest3)=(1, -1, 3) is not present in table "pktable".
+DETAIL: Key (ftest1, ftest2, ftest3)=(0, -1, -2) is not present in table "pktable".
-- Try to update something that will set default
-UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
+UPDATE PKTABLE set ptest1=0, ptest2=-1, ptest3=-2 where ptest2=2;
UPDATE PKTABLE set ptest2=10 where ptest2=4;
-- Try to update something that should not set default
UPDATE PKTABLE set ptest2=2 WHERE ptest2=3 and ptest1=1;
--------+--------+--------+--------
2 | 3 | 4 | test3
2 | -1 | 5 | test5
- 0 | 5 | 10 | test1
+ 0 | -1 | -2 | test1
2 | 10 | 5 | test4
1 | 2 | 3 | test2
(5 rows)
2 | | 3 | 3
| 2 | 7 | 4
| 3 | 4 | 5
- 0 | -1 | | 1
- 2 | -1 | 5 | 1
+ 0 | -1 | -2 | 1
+ 0 | -1 | -2 | 1
(7 rows)
-- Try to delete something that should set null
ptest1 | ptest2 | ptest3 | ptest4
--------+--------+--------+--------
2 | -1 | 5 | test5
- 0 | 5 | 10 | test1
+ 0 | -1 | -2 | test1
2 | 10 | 5 | test4
1 | 2 | 3 | test2
(4 rows)
2 | | 3 | 3
| 2 | 7 | 4
| 3 | 4 | 5
- 0 | -1 | | 1
- 2 | -1 | 5 | 1
+ 0 | -1 | -2 | 1
+ 0 | -1 | -2 | 1
| | | 1
(7 rows)
-- Try to delete something that should not set null
-DELETE FROM PKTABLE where ptest2=5;
+DELETE FROM PKTABLE where ptest2=-1 and ptest3=5;
-- Show PKTABLE and FKTABLE
SELECT * from PKTABLE;
ptest1 | ptest2 | ptest3 | ptest4
--------+--------+--------+--------
- 2 | -1 | 5 | test5
+ 0 | -1 | -2 | test1
2 | 10 | 5 | test4
1 | 2 | 3 | test2
(3 rows)
2 | | 3 | 3
| 2 | 7 | 4
| 3 | 4 | 5
- 0 | -1 | | 1
- 2 | -1 | 5 | 1
+ 0 | -1 | -2 | 1
+ 0 | -1 | -2 | 1
| | | 1
(7 rows)
-- set default update / set null delete
CREATE TABLE PKTABLE ( ptest1 int, ptest2 int, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2, ptest3) );
-CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int DEFAULT -1, ftest3 int, ftest4 int, CONSTRAINT constrname3
+CREATE TABLE FKTABLE ( ftest1 int DEFAULT 0, ftest2 int DEFAULT -1, ftest3 int DEFAULT -2, ftest4 int, CONSTRAINT constrname3
FOREIGN KEY(ftest1, ftest2, ftest3) REFERENCES PKTABLE
ON DELETE SET NULL ON UPDATE SET DEFAULT);
UPDATE PKTABLE set ptest2=5 where ptest2=2;
-- Try to update something that will set default
-UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
+UPDATE PKTABLE set ptest1=0, ptest2=-1, ptest3=-2 where ptest2=2;
UPDATE PKTABLE set ptest2=10 where ptest2=4;
-- Try to update something that should not set default
SELECT * from FKTABLE;
-- Try to delete something that should not set null
-DELETE FROM PKTABLE where ptest2=5;
+DELETE FROM PKTABLE where ptest2=-1 and ptest3=5;
-- Show PKTABLE and FKTABLE
SELECT * from PKTABLE;