* ----------
*/
-
-/* ----------
- * Internal TODO:
- *
- * Add MATCH PARTIAL logic.
- * ----------
- */
-
#include "postgres.h"
#include "access/heapam.h"
fk_rel = trigdata->tg_relation;
pk_rel = table_open(riinfo->pk_relid, RowShareLock);
- if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
-
switch (ri_NullCheck(RelationGetDescr(fk_rel), newslot, riinfo, false))
{
case RI_KEYS_ALL_NULL:
table_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
+#ifdef NOT_USED
case FKCONSTR_MATCH_PARTIAL:
/*
* to only include non-null columns, or by writing a
* special version here)
*/
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- table_close(pk_rel, RowShareLock);
- return PointerGetDatum(NULL);
-
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
break;
+#endif
}
case RI_KEYS_NONE_NULL:
pk_rel = trigdata->tg_relation;
old_slot = trigdata->tg_trigslot;
- switch (riinfo->confmatchtype)
+ /*
+ * If another PK row now exists providing the old key values, we
+ * should not do anything. However, this check should only be
+ * made in the NO ACTION case; in RESTRICT cases we don't wish to
+ * allow another row to be substituted.
+ */
+ if (is_no_action &&
+ ri_Check_Pk_Match(pk_rel, fk_rel, old_slot, riinfo))
{
- /* ----------
- * SQL:2008 15.17 <Execution of referential actions>
- * General rules 9) a) iv):
- * MATCH SIMPLE/FULL
- * ... ON DELETE RESTRICT
- * General rules 10) a) iv):
- * MATCH SIMPLE/FULL
- * ... ON UPDATE RESTRICT
- * ----------
- */
- case FKCONSTR_MATCH_SIMPLE:
- case FKCONSTR_MATCH_FULL:
-
- /*
- * If another PK row now exists providing the old key values, we
- * should not do anything. However, this check should only be
- * made in the NO ACTION case; in RESTRICT cases we don't wish to
- * allow another row to be substituted.
- */
- if (is_no_action &&
- ri_Check_Pk_Match(pk_rel, fk_rel, old_slot, riinfo))
- {
- table_close(fk_rel, RowShareLock);
- return PointerGetDatum(NULL);
- }
-
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "SPI_connect failed");
+ table_close(fk_rel, RowShareLock);
+ return PointerGetDatum(NULL);
+ }
- /*
- * Fetch or prepare a saved plan for the restrict lookup (it's the
- * same query for delete and update cases)
- */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_CHECKREF);
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
- if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
- {
- StringInfoData querybuf;
- char fkrelname[MAX_QUOTED_REL_NAME_LEN];
- char attname[MAX_QUOTED_NAME_LEN];
- char paramname[16];
- const char *querysep;
- Oid queryoids[RI_MAX_NUMKEYS];
- const char *fk_only;
- int i;
-
- /* ----------
- * The query string built is
- * SELECT 1 FROM [ONLY] <fktable> x WHERE $1 = fkatt1 [AND ...]
- * FOR KEY SHARE OF x
- * The type id's for the $ parameters are those of the
- * corresponding PK attributes.
- * ----------
- */
- initStringInfo(&querybuf);
- fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
- "" : "ONLY ";
- quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
- fk_only, fkrelname);
- querysep = "WHERE";
- for (i = 0; i < riinfo->nkeys; i++)
- {
- Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
- Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-
- quoteOneName(attname,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
- sprintf(paramname, "$%d", i + 1);
- ri_GenerateQual(&querybuf, querysep,
- paramname, pk_type,
- riinfo->pf_eq_oprs[i],
- attname, fk_type);
- querysep = "AND";
- queryoids[i] = pk_type;
- }
- appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
+ /*
+ * Fetch or prepare a saved plan for the restrict lookup (it's the
+ * same query for delete and update cases)
+ */
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_CHECKREF);
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
- }
+ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
+ {
+ StringInfoData querybuf;
+ char fkrelname[MAX_QUOTED_REL_NAME_LEN];
+ char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
+ const char *querysep;
+ Oid queryoids[RI_MAX_NUMKEYS];
+ const char *fk_only;
+ int i;
- /*
- * We have a plan now. Run it to check for existing references.
- */
- ri_PerformCheck(riinfo, &qkey, qplan,
- fk_rel, pk_rel,
- old_slot, NULL,
- true, /* must detect new rows */
- SPI_OK_SELECT);
+ /* ----------
+ * The query string built is
+ * SELECT 1 FROM [ONLY] <fktable> x WHERE $1 = fkatt1 [AND ...]
+ * FOR KEY SHARE OF x
+ * The type id's for the $ parameters are those of the
+ * corresponding PK attributes.
+ * ----------
+ */
+ initStringInfo(&querybuf);
+ fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
+ "" : "ONLY ";
+ quoteRelationName(fkrelname, fk_rel);
+ appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
+ fk_only, fkrelname);
+ querysep = "WHERE";
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
- if (SPI_finish() != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed");
+ quoteOneName(attname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo->pf_eq_oprs[i],
+ attname, fk_type);
+ querysep = "AND";
+ queryoids[i] = pk_type;
+ }
+ appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- table_close(fk_rel, RowShareLock);
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
- return PointerGetDatum(NULL);
+ /*
+ * We have a plan now. Run it to check for existing references.
+ */
+ ri_PerformCheck(riinfo, &qkey, qplan,
+ fk_rel, pk_rel,
+ old_slot, NULL,
+ true, /* must detect new rows */
+ SPI_OK_SELECT);
- /*
- * Handle MATCH PARTIAL restrict delete or update.
- */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- return PointerGetDatum(NULL);
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ table_close(fk_rel, RowShareLock);
- /* Never reached */
return PointerGetDatum(NULL);
}
pk_rel = trigdata->tg_relation;
old_slot = trigdata->tg_trigslot;
- switch (riinfo->confmatchtype)
- {
- /* ----------
- * SQL:2008 15.17 <Execution of referential actions>
- * General rules 9) a) i):
- * MATCH SIMPLE/FULL
- * ... ON DELETE CASCADE
- * ----------
- */
- case FKCONSTR_MATCH_SIMPLE:
- case FKCONSTR_MATCH_FULL:
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "SPI_connect failed");
-
- /*
- * Fetch or prepare a saved plan for the cascaded delete
- */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
- if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
- {
- StringInfoData querybuf;
- char fkrelname[MAX_QUOTED_REL_NAME_LEN];
- char attname[MAX_QUOTED_NAME_LEN];
- char paramname[16];
- const char *querysep;
- Oid queryoids[RI_MAX_NUMKEYS];
- const char *fk_only;
-
- /* ----------
- * The query string built is
- * DELETE FROM [ONLY] <fktable> WHERE $1 = fkatt1 [AND ...]
- * The type id's for the $ parameters are those of the
- * corresponding PK attributes.
- * ----------
- */
- initStringInfo(&querybuf);
- fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
- "" : "ONLY ";
- quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf, "DELETE FROM %s%s",
- fk_only, fkrelname);
- querysep = "WHERE";
- for (i = 0; i < riinfo->nkeys; i++)
- {
- Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
- Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-
- quoteOneName(attname,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
- sprintf(paramname, "$%d", i + 1);
- ri_GenerateQual(&querybuf, querysep,
- paramname, pk_type,
- riinfo->pf_eq_oprs[i],
- attname, fk_type);
- querysep = "AND";
- queryoids[i] = pk_type;
- }
+ /*
+ * Fetch or prepare a saved plan for the cascaded delete
+ */
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
- }
+ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
+ {
+ StringInfoData querybuf;
+ char fkrelname[MAX_QUOTED_REL_NAME_LEN];
+ char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
+ const char *querysep;
+ Oid queryoids[RI_MAX_NUMKEYS];
+ const char *fk_only;
- /*
- * We have a plan now. Build up the arguments from the key values
- * in the deleted PK tuple and delete the referencing rows
- */
- ri_PerformCheck(riinfo, &qkey, qplan,
- fk_rel, pk_rel,
- old_slot, NULL,
- true, /* must detect new rows */
- SPI_OK_DELETE);
+ /* ----------
+ * The query string built is
+ * DELETE FROM [ONLY] <fktable> WHERE $1 = fkatt1 [AND ...]
+ * The type id's for the $ parameters are those of the
+ * corresponding PK attributes.
+ * ----------
+ */
+ initStringInfo(&querybuf);
+ fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
+ "" : "ONLY ";
+ quoteRelationName(fkrelname, fk_rel);
+ appendStringInfo(&querybuf, "DELETE FROM %s%s",
+ fk_only, fkrelname);
+ querysep = "WHERE";
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
- if (SPI_finish() != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed");
+ quoteOneName(attname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo->pf_eq_oprs[i],
+ attname, fk_type);
+ querysep = "AND";
+ queryoids[i] = pk_type;
+ }
- table_close(fk_rel, RowExclusiveLock);
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
- return PointerGetDatum(NULL);
+ /*
+ * We have a plan now. Build up the arguments from the key values
+ * in the deleted PK tuple and delete the referencing rows
+ */
+ ri_PerformCheck(riinfo, &qkey, qplan,
+ fk_rel, pk_rel,
+ old_slot, NULL,
+ true, /* must detect new rows */
+ SPI_OK_DELETE);
- /*
- * Handle MATCH PARTIAL cascaded delete.
- */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- return PointerGetDatum(NULL);
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ table_close(fk_rel, RowExclusiveLock);
- /* Never reached */
return PointerGetDatum(NULL);
}
new_slot = trigdata->tg_newslot;
old_slot = trigdata->tg_trigslot;
- switch (riinfo->confmatchtype)
- {
- /* ----------
- * SQL:2008 15.17 <Execution of referential actions>
- * General rules 10) a) i):
- * MATCH SIMPLE/FULL
- * ... ON UPDATE CASCADE
- * ----------
- */
- case FKCONSTR_MATCH_SIMPLE:
- case FKCONSTR_MATCH_FULL:
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "SPI_connect failed");
-
- /*
- * Fetch or prepare a saved plan for the cascaded update
- */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
- if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
- {
- StringInfoData querybuf;
- StringInfoData qualbuf;
- char fkrelname[MAX_QUOTED_REL_NAME_LEN];
- char attname[MAX_QUOTED_NAME_LEN];
- char paramname[16];
- const char *querysep;
- const char *qualsep;
- Oid queryoids[RI_MAX_NUMKEYS * 2];
- const char *fk_only;
-
- /* ----------
- * The query string built is
- * UPDATE [ONLY] <fktable> SET fkatt1 = $1 [, ...]
- * WHERE $n = fkatt1 [AND ...]
- * The type id's for the $ parameters are those of the
- * corresponding PK attributes. Note that we are assuming
- * there is an assignment cast from the PK to the FK type;
- * else the parser will fail.
- * ----------
- */
- initStringInfo(&querybuf);
- initStringInfo(&qualbuf);
- fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
- "" : "ONLY ";
- quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf, "UPDATE %s%s SET",
- fk_only, fkrelname);
- querysep = "";
- qualsep = "WHERE";
- for (i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
- {
- Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
- Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-
- quoteOneName(attname,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf,
- "%s %s = $%d",
- querysep, attname, i + 1);
- sprintf(paramname, "$%d", j + 1);
- ri_GenerateQual(&qualbuf, qualsep,
- paramname, pk_type,
- riinfo->pf_eq_oprs[i],
- attname, fk_type);
- querysep = ",";
- qualsep = "AND";
- queryoids[i] = pk_type;
- queryoids[j] = pk_type;
- }
- appendStringInfoString(&querybuf, qualbuf.data);
+ /*
+ * Fetch or prepare a saved plan for the cascaded update
+ */
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
- &qkey, fk_rel, pk_rel, true);
- }
+ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
+ {
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
+ char fkrelname[MAX_QUOTED_REL_NAME_LEN];
+ char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
+ const char *querysep;
+ const char *qualsep;
+ Oid queryoids[RI_MAX_NUMKEYS * 2];
+ const char *fk_only;
- /*
- * We have a plan now. Run it to update the existing references.
- */
- ri_PerformCheck(riinfo, &qkey, qplan,
- fk_rel, pk_rel,
- old_slot, new_slot,
- true, /* must detect new rows */
- SPI_OK_UPDATE);
+ /* ----------
+ * The query string built is
+ * UPDATE [ONLY] <fktable> SET fkatt1 = $1 [, ...]
+ * WHERE $n = fkatt1 [AND ...]
+ * The type id's for the $ parameters are those of the
+ * corresponding PK attributes. Note that we are assuming
+ * there is an assignment cast from the PK to the FK type;
+ * else the parser will fail.
+ * ----------
+ */
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
+ fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
+ "" : "ONLY ";
+ quoteRelationName(fkrelname, fk_rel);
+ appendStringInfo(&querybuf, "UPDATE %s%s SET",
+ fk_only, fkrelname);
+ querysep = "";
+ qualsep = "WHERE";
+ for (i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
+ {
+ Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
- if (SPI_finish() != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed");
+ quoteOneName(attname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ appendStringInfo(&querybuf,
+ "%s %s = $%d",
+ querysep, attname, i + 1);
+ sprintf(paramname, "$%d", j + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo->pf_eq_oprs[i],
+ attname, fk_type);
+ querysep = ",";
+ qualsep = "AND";
+ queryoids[i] = pk_type;
+ queryoids[j] = pk_type;
+ }
+ appendStringInfoString(&querybuf, qualbuf.data);
- table_close(fk_rel, RowExclusiveLock);
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
- return PointerGetDatum(NULL);
+ /*
+ * We have a plan now. Run it to update the existing references.
+ */
+ ri_PerformCheck(riinfo, &qkey, qplan,
+ fk_rel, pk_rel,
+ old_slot, new_slot,
+ true, /* must detect new rows */
+ SPI_OK_UPDATE);
- /*
- * Handle MATCH PARTIAL cascade update.
- */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- return PointerGetDatum(NULL);
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ table_close(fk_rel, RowExclusiveLock);
- /* Never reached */
return PointerGetDatum(NULL);
}
pk_rel = trigdata->tg_relation;
old_slot = trigdata->tg_trigslot;
- switch (riinfo->confmatchtype)
- {
- /* ----------
- * SQL:2008 15.17 <Execution of referential actions>
- * General rules 9) a) ii):
- * MATCH SIMPLE/FULL
- * ... ON DELETE SET NULL
- * General rules 10) a) ii):
- * MATCH SIMPLE/FULL
- * ... ON UPDATE SET NULL
- * ----------
- */
- case FKCONSTR_MATCH_SIMPLE:
- case FKCONSTR_MATCH_FULL:
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "SPI_connect failed");
-
- /*
- * Fetch or prepare a saved plan for the set null operation (it's
- * the same query for delete and update cases)
- */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_DOUPDATE);
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
- if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
- {
- StringInfoData querybuf;
- StringInfoData qualbuf;
- char fkrelname[MAX_QUOTED_REL_NAME_LEN];
- char attname[MAX_QUOTED_NAME_LEN];
- char paramname[16];
- const char *querysep;
- const char *qualsep;
- const char *fk_only;
- Oid queryoids[RI_MAX_NUMKEYS];
-
- /* ----------
- * The query string built is
- * UPDATE [ONLY] <fktable> SET fkatt1 = NULL [, ...]
- * WHERE $1 = fkatt1 [AND ...]
- * The type id's for the $ parameters are those of the
- * corresponding PK attributes.
- * ----------
- */
- initStringInfo(&querybuf);
- initStringInfo(&qualbuf);
- fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
- "" : "ONLY ";
- quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf, "UPDATE %s%s SET",
- fk_only, fkrelname);
- querysep = "";
- qualsep = "WHERE";
- for (i = 0; i < riinfo->nkeys; i++)
- {
- Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
- Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-
- quoteOneName(attname,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf,
- "%s %s = 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);
+ /*
+ * Fetch or prepare a saved plan for the set null operation (it's
+ * the same query for delete and update cases)
+ */
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_DOUPDATE);
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
- }
+ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
+ {
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
+ char fkrelname[MAX_QUOTED_REL_NAME_LEN];
+ char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
+ const char *querysep;
+ const char *qualsep;
+ const char *fk_only;
+ Oid queryoids[RI_MAX_NUMKEYS];
- /*
- * We have a plan now. Run it to update the existing references.
- */
- ri_PerformCheck(riinfo, &qkey, qplan,
- fk_rel, pk_rel,
- old_slot, NULL,
- true, /* must detect new rows */
- SPI_OK_UPDATE);
+ /* ----------
+ * The query string built is
+ * UPDATE [ONLY] <fktable> SET fkatt1 = NULL [, ...]
+ * WHERE $1 = fkatt1 [AND ...]
+ * The type id's for the $ parameters are those of the
+ * corresponding PK attributes.
+ * ----------
+ */
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
+ fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
+ "" : "ONLY ";
+ quoteRelationName(fkrelname, fk_rel);
+ appendStringInfo(&querybuf, "UPDATE %s%s SET",
+ fk_only, fkrelname);
+ querysep = "";
+ qualsep = "WHERE";
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
- if (SPI_finish() != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed");
+ quoteOneName(attname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ 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);
- table_close(fk_rel, RowExclusiveLock);
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
- return PointerGetDatum(NULL);
+ /*
+ * We have a plan now. Run it to update the existing references.
+ */
+ ri_PerformCheck(riinfo, &qkey, qplan,
+ fk_rel, pk_rel,
+ old_slot, NULL,
+ true, /* must detect new rows */
+ SPI_OK_UPDATE);
- /*
- * Handle MATCH PARTIAL set null delete or update.
- */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- return PointerGetDatum(NULL);
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ table_close(fk_rel, RowExclusiveLock);
- /* Never reached */
return PointerGetDatum(NULL);
}
pk_rel = trigdata->tg_relation;
old_slot = trigdata->tg_trigslot;
- switch (riinfo->confmatchtype)
- {
- /* ----------
- * SQL:2008 15.17 <Execution of referential actions>
- * General rules 9) a) iii):
- * MATCH SIMPLE/FULL
- * ... ON DELETE SET DEFAULT
- * General rules 10) a) iii):
- * MATCH SIMPLE/FULL
- * ... ON UPDATE SET DEFAULT
- * ----------
- */
- case FKCONSTR_MATCH_SIMPLE:
- case FKCONSTR_MATCH_FULL:
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "SPI_connect failed");
-
- /*
- * Fetch or prepare a saved plan for the set default operation
- * (it's the same query for delete and update cases)
- */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_DOUPDATE);
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect failed");
- if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
- {
- StringInfoData querybuf;
- StringInfoData qualbuf;
- char fkrelname[MAX_QUOTED_REL_NAME_LEN];
- char attname[MAX_QUOTED_NAME_LEN];
- char paramname[16];
- const char *querysep;
- const char *qualsep;
- Oid queryoids[RI_MAX_NUMKEYS];
- const char *fk_only;
- int i;
-
- /* ----------
- * The query string built is
- * UPDATE [ONLY] <fktable> SET fkatt1 = DEFAULT [, ...]
- * WHERE $1 = fkatt1 [AND ...]
- * The type id's for the $ parameters are those of the
- * corresponding PK attributes.
- * ----------
- */
- initStringInfo(&querybuf);
- initStringInfo(&qualbuf);
- quoteRelationName(fkrelname, fk_rel);
- fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
- "" : "ONLY ";
- appendStringInfo(&querybuf, "UPDATE %s%s SET",
- fk_only, fkrelname);
- querysep = "";
- qualsep = "WHERE";
- for (i = 0; i < riinfo->nkeys; i++)
- {
- Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
- Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-
- quoteOneName(attname,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf,
- "%s %s = DEFAULT",
- querysep, attname);
- sprintf(paramname, "$%d", i + 1);
- ri_GenerateQual(&qualbuf, qualsep,
- paramname, pk_type,
- riinfo->pf_eq_oprs[i],
- attname, fk_type);
- querysep = ",";
- qualsep = "AND";
- queryoids[i] = pk_type;
- }
- appendStringInfoString(&querybuf, qualbuf.data);
+ /*
+ * Fetch or prepare a saved plan for the set default operation
+ * (it's the same query for delete and update cases)
+ */
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_DOUPDATE);
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
- }
+ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
+ {
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
+ char fkrelname[MAX_QUOTED_REL_NAME_LEN];
+ char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
+ const char *querysep;
+ const char *qualsep;
+ Oid queryoids[RI_MAX_NUMKEYS];
+ const char *fk_only;
+ int i;
- /*
- * We have a plan now. Run it to update the existing references.
- */
- ri_PerformCheck(riinfo, &qkey, qplan,
- fk_rel, pk_rel,
- old_slot, NULL,
- true, /* must detect new rows */
- SPI_OK_UPDATE);
+ /* ----------
+ * The query string built is
+ * UPDATE [ONLY] <fktable> SET fkatt1 = DEFAULT [, ...]
+ * WHERE $1 = fkatt1 [AND ...]
+ * The type id's for the $ parameters are those of the
+ * corresponding PK attributes.
+ * ----------
+ */
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
+ quoteRelationName(fkrelname, fk_rel);
+ fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
+ "" : "ONLY ";
+ appendStringInfo(&querybuf, "UPDATE %s%s SET",
+ fk_only, fkrelname);
+ querysep = "";
+ qualsep = "WHERE";
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
- if (SPI_finish() != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed");
+ quoteOneName(attname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ appendStringInfo(&querybuf,
+ "%s %s = DEFAULT",
+ querysep, attname);
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo->pf_eq_oprs[i],
+ attname, fk_type);
+ querysep = ",";
+ qualsep = "AND";
+ queryoids[i] = pk_type;
+ }
+ appendStringInfoString(&querybuf, qualbuf.data);
- table_close(fk_rel, RowExclusiveLock);
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
- /*
- * If we just deleted or updated the PK row whose key was equal to
- * the FK columns' default values, and a referencing row exists in
- * the FK table, we would have updated that row to the same values
- * it already had --- and RI_FKey_fk_upd_check_required would
- * hence believe no check is necessary. So we need to do another
- * lookup now and in case a reference still exists, abort the
- * operation. That is already implemented in the NO ACTION
- * trigger, so just run it. (This recheck is only needed in the
- * SET DEFAULT case, since CASCADE would remove such rows in case
- * of a DELETE operation or would change the FK key values in case
- * of an UPDATE, while SET NULL is certain to result in rows that
- * satisfy the FK constraint.)
- */
- return ri_restrict(trigdata, true);
+ /*
+ * We have a plan now. Run it to update the existing references.
+ */
+ ri_PerformCheck(riinfo, &qkey, qplan,
+ fk_rel, pk_rel,
+ old_slot, NULL,
+ true, /* must detect new rows */
+ SPI_OK_UPDATE);
- /*
- * Handle MATCH PARTIAL set default delete or update.
- */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- return PointerGetDatum(NULL);
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish failed");
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ table_close(fk_rel, RowExclusiveLock);
- /* Never reached */
- return PointerGetDatum(NULL);
+ /*
+ * If we just deleted or updated the PK row whose key was equal to
+ * the FK columns' default values, and a referencing row exists in
+ * the FK table, we would have updated that row to the same values
+ * it already had --- and RI_FKey_fk_upd_check_required would
+ * hence believe no check is necessary. So we need to do another
+ * lookup now and in case a reference still exists, abort the
+ * operation. That is already implemented in the NO ACTION
+ * trigger, so just run it. (This recheck is only needed in the
+ * SET DEFAULT case, since CASCADE would remove such rows in case
+ * of a DELETE operation or would change the FK key values in case
+ * of an UPDATE, while SET NULL is certain to result in rows that
+ * satisfy the FK constraint.)
+ */
+ return ri_restrict(trigdata, true);
}
*/
riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);
- switch (riinfo->confmatchtype)
- {
- case FKCONSTR_MATCH_SIMPLE:
- case FKCONSTR_MATCH_FULL:
-
- /*
- * If any old key value is NULL, the row could not have been
- * referenced by an FK row, so no check is needed.
- */
- if (ri_NullCheck(RelationGetDescr(pk_rel), old_slot, riinfo, true) != RI_KEYS_NONE_NULL)
- return false;
-
- /* If all old and new key values are equal, no check is needed */
- if (new_slot && ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true))
- return false;
-
- /* Else we need to fire the trigger. */
- return true;
-
- /* Handle MATCH PARTIAL check. */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- break;
+ /*
+ * If any old key value is NULL, the row could not have been
+ * referenced by an FK row, so no check is needed.
+ */
+ if (ri_NullCheck(RelationGetDescr(pk_rel), old_slot, riinfo, true) != RI_KEYS_NONE_NULL)
+ return false;
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ /* If all old and new key values are equal, no check is needed */
+ if (new_slot && ri_KeysEqual(pk_rel, old_slot, new_slot, riinfo, true))
+ return false;
- /* Never reached */
- return false;
+ /* Else we need to fire the trigger. */
+ return true;
}
/* ----------
TupleTableSlot *old_slot, TupleTableSlot *new_slot)
{
const RI_ConstraintInfo *riinfo;
+ int ri_nullcheck;
Datum xminDatum;
TransactionId xmin;
bool isnull;
*/
riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
- switch (riinfo->confmatchtype)
- {
- case FKCONSTR_MATCH_SIMPLE:
-
- /*
- * If any new key value is NULL, the row must satisfy the
- * constraint, so no check is needed.
- */
- if (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false) != RI_KEYS_NONE_NULL)
- return false;
-
- /*
- * If the original row was inserted by our own transaction, we
- * must fire the trigger whether or not the keys are equal. This
- * is because our UPDATE will invalidate the INSERT so that the
- * INSERT RI trigger will not do anything; so we had better do the
- * UPDATE check. (We could skip this if we knew the INSERT
- * trigger already fired, but there is no easy way to know that.)
- */
- xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
- Assert(!isnull);
- xmin = DatumGetTransactionId(xminDatum);
- if (TransactionIdIsCurrentTransactionId(xmin))
- return true;
+ ri_nullcheck = ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false);
- /* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
+ /*
+ * If all new key values are NULL, the row satisfies the constraint, so no
+ * check is needed.
+ */
+ if (ri_nullcheck == RI_KEYS_ALL_NULL)
+ return false;
+ /*
+ * If some new key values are NULL, the behavior depends on the match type.
+ */
+ else if (ri_nullcheck == RI_KEYS_SOME_NULL)
+ {
+ switch (riinfo->confmatchtype)
+ {
+ case FKCONSTR_MATCH_SIMPLE:
+ /*
+ * If any new key value is NULL, the row must satisfy the
+ * constraint, so no check is needed.
+ */
return false;
- /* Else we need to fire the trigger. */
- return true;
-
- case FKCONSTR_MATCH_FULL:
-
- /*
- * If all new key values are NULL, the row must satisfy the
- * constraint, so no check is needed. On the other hand, if only
- * some of them are NULL, the row must fail the constraint. We
- * must not throw error here, because the row might get
- * invalidated before the constraint is to be checked, but we
- * should queue the event to apply the check later.
- */
- switch (ri_NullCheck(RelationGetDescr(fk_rel), new_slot, riinfo, false))
- {
- case RI_KEYS_ALL_NULL:
- return false;
- case RI_KEYS_SOME_NULL:
- return true;
- case RI_KEYS_NONE_NULL:
- break; /* continue with the check */
- }
+ case FKCONSTR_MATCH_PARTIAL:
+ /*
+ * Don't know, must run full check.
+ */
+ break;
- /*
- * If the original row was inserted by our own transaction, we
- * must fire the trigger whether or not the keys are equal. This
- * is because our UPDATE will invalidate the INSERT so that the
- * INSERT RI trigger will not do anything; so we had better do the
- * UPDATE check. (We could skip this if we knew the INSERT
- * trigger already fired, but there is no easy way to know that.)
- */
- xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
- Assert(!isnull);
- xmin = DatumGetTransactionId(xminDatum);
- if (TransactionIdIsCurrentTransactionId(xmin))
+ case FKCONSTR_MATCH_FULL:
+ /*
+ * If some new key values are NULL, the row fails the
+ * constraint. We must not throw error here, because the row
+ * might get invalidated before the constraint is to be
+ * checked, but we should queue the event to apply the check
+ * later.
+ */
return true;
+ }
+ }
- /* If all old and new key values are equal, no check is needed */
- if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
- return false;
-
- /* Else we need to fire the trigger. */
- return true;
+ /*
+ * Continues here for no new key values are NULL, or we couldn't decide
+ * yet.
+ */
- /* Handle MATCH PARTIAL check. */
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- break;
+ /*
+ * If the original row was inserted by our own transaction, we
+ * must fire the trigger whether or not the keys are equal. This
+ * is because our UPDATE will invalidate the INSERT so that the
+ * INSERT RI trigger will not do anything; so we had better do the
+ * UPDATE check. (We could skip this if we knew the INSERT
+ * trigger already fired, but there is no easy way to know that.)
+ */
+ xminDatum = slot_getsysattr(old_slot, MinTransactionIdAttributeNumber, &isnull);
+ Assert(!isnull);
+ xmin = DatumGetTransactionId(xminDatum);
+ if (TransactionIdIsCurrentTransactionId(xmin))
+ return true;
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
- }
+ /* If all old and new key values are equal, no check is needed */
+ if (ri_KeysEqual(fk_rel, old_slot, new_slot, riinfo, false))
+ return false;
- /* Never reached */
- return false;
+ /* Else we need to fire the trigger. */
+ return true;
}
/* ----------
case FKCONSTR_MATCH_FULL:
sep = " OR ";
break;
- case FKCONSTR_MATCH_PARTIAL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("MATCH PARTIAL not yet implemented")));
- break;
- default:
- elog(ERROR, "unrecognized confmatchtype: %d",
- riinfo->confmatchtype);
- break;
}
}
appendStringInfoChar(&querybuf, ')');
trigger->tgname, RelationGetRelationName(trig_rel));
}
+ if (riinfo->confmatchtype != FKCONSTR_MATCH_FULL &&
+ riinfo->confmatchtype != FKCONSTR_MATCH_PARTIAL &&
+ riinfo->confmatchtype != FKCONSTR_MATCH_SIMPLE)
+ elog(ERROR, "unrecognized confmatchtype: %d",
+ riinfo->confmatchtype);
+
+ if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("MATCH PARTIAL not yet implemented")));
+
return riinfo;
}