From dfd9c116cc3ebaf42f895b9b16a9ff69bb21664b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 20 Jun 2012 20:15:02 -0400 Subject: [PATCH] Remove incomplete/incorrect support for zero-column foreign keys. The original coding in ri_triggers.c had partial support for the concept of zero-column foreign key constraints. But this is not defined in the SQL standard, nor was it ever allowed by any other part of Postgres, nor was it very fully implemented even here (eg there was no support for preventing PK-table deletions that would violate the constraint). Doesn't seem very useful to carry 100-plus lines of code for a corner case that no one is interested in making work. Instead, just add a check that the column list read from pg_constraint is non-empty. --- src/backend/utils/adt/ri_triggers.c | 154 ++-------------------------- 1 file changed, 7 insertions(+), 147 deletions(-) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index c24396724f..983f631234 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -303,60 +303,6 @@ RI_FKey_check(TriggerData *trigdata) fk_rel = trigdata->tg_relation; pk_rel = heap_open(riinfo->pk_relid, RowShareLock); - /* ---------- - * SQL:2008 4.17.3 - * If Rf and Rt are empty (no columns to compare given) - * constraint is true if 0 < (SELECT COUNT(*) FROM T) - * - * Note: The special case that no columns are given cannot - * occur at present in Postgres (and is disallowed by the - * standard too); it's just there for future enhancements. - * ---------- - */ - if (riinfo->nkeys == 0) - { - if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "SPI_connect failed"); - - ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK); - - if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) - { - char querystr[MAX_QUOTED_REL_NAME_LEN + 100]; - char pkrelname[MAX_QUOTED_REL_NAME_LEN]; - - /* --------- - * The query string built is - * SELECT 1 FROM ONLY - * ---------- - */ - quoteRelationName(pkrelname, pk_rel); - snprintf(querystr, sizeof(querystr), - "SELECT 1 FROM ONLY %s x FOR SHARE OF x", - pkrelname); - - /* Prepare and save the plan */ - qplan = ri_PlanCheck(querystr, 0, NULL, - &qkey, fk_rel, pk_rel, true); - } - - /* - * Execute the plan - */ - ri_PerformCheck(riinfo, &qkey, qplan, - fk_rel, pk_rel, - NULL, NULL, - false, - SPI_OK_SELECT); - - if (SPI_finish() != SPI_OK_FINISH) - elog(ERROR, "SPI_finish failed"); - - heap_close(pk_rel, RowShareLock); - - return PointerGetDatum(NULL); - } - if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -704,12 +650,6 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the old tuple. * @@ -922,12 +862,6 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the new and * old tuple. @@ -1109,12 +1043,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the old tuple. * @@ -1273,12 +1201,6 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the new and * old tuple. @@ -1458,12 +1380,6 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the old tuple. * @@ -1630,12 +1546,6 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the old tuple. * @@ -1810,12 +1720,6 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the old tuple. * @@ -1997,12 +1901,6 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger, trigdata->tg_relation, true); - /* - * Nothing to do if no column names to compare given - */ - if (riinfo->nkeys == 0) - return PointerGetDatum(NULL); - /* * Get the relation descriptors of the FK and PK tables and the old tuple. * @@ -2186,13 +2084,6 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel, */ riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true); - /* - * Nothing to do if no columns (satisfaction of such a constraint only - * requires existence of a PK row, and this update won't change that). - */ - if (riinfo->nkeys == 0) - return false; - switch (riinfo->confmatchtype) { case FKCONSTR_MATCH_SIMPLE: @@ -2250,13 +2141,6 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel, */ riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false); - /* - * Nothing to do if no columns (satisfaction of such a constraint only - * requires existence of a PK row, and this update won't change that). - */ - if (riinfo->nkeys == 0) - return false; - switch (riinfo->confmatchtype) { case FKCONSTR_MATCH_SIMPLE: @@ -2945,13 +2829,13 @@ ri_LoadConstraintInfo(Oid constraintOid) if (isNull) elog(ERROR, "null conkey for constraint %u", constraintOid); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || - numkeys < 0 || - numkeys > RI_MAX_NUMKEYS || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != INT2OID) elog(ERROR, "conkey is not a 1-D smallint array"); + numkeys = ARR_DIMS(arr)[0]; + if (numkeys <= 0 || numkeys > RI_MAX_NUMKEYS) + elog(ERROR, "foreign key constraint cannot have %d columns", numkeys); riinfo->nkeys = numkeys; memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16)); if ((Pointer) arr != DatumGetPointer(adatum)) @@ -2962,10 +2846,8 @@ ri_LoadConstraintInfo(Oid constraintOid) if (isNull) elog(ERROR, "null confkey for constraint %u", constraintOid); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || - numkeys != riinfo->nkeys || - numkeys > RI_MAX_NUMKEYS || + ARR_DIMS(arr)[0] != numkeys || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != INT2OID) elog(ERROR, "confkey is not a 1-D smallint array"); @@ -2978,11 +2860,9 @@ ri_LoadConstraintInfo(Oid constraintOid) if (isNull) elog(ERROR, "null conpfeqop for constraint %u", constraintOid); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - numkeys = ARR_DIMS(arr)[0]; /* see TryReuseForeignKey if you change the test below */ if (ARR_NDIM(arr) != 1 || - numkeys != riinfo->nkeys || - numkeys > RI_MAX_NUMKEYS || + ARR_DIMS(arr)[0] != numkeys || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "conpfeqop is not a 1-D Oid array"); @@ -2995,10 +2875,8 @@ ri_LoadConstraintInfo(Oid constraintOid) if (isNull) elog(ERROR, "null conppeqop for constraint %u", constraintOid); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || - numkeys != riinfo->nkeys || - numkeys > RI_MAX_NUMKEYS || + ARR_DIMS(arr)[0] != numkeys || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "conppeqop is not a 1-D Oid array"); @@ -3011,10 +2889,8 @@ ri_LoadConstraintInfo(Oid constraintOid) if (isNull) elog(ERROR, "null conffeqop for constraint %u", constraintOid); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ - numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || - numkeys != riinfo->nkeys || - numkeys > RI_MAX_NUMKEYS || + ARR_DIMS(arr)[0] != numkeys || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != OIDOID) elog(ERROR, "conffeqop is not a 1-D Oid array"); @@ -3311,22 +3187,6 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo, tupdesc = pk_rel->rd_att; } - /* - * Special case - if there are no keys at all, this is a 'no column' - * constraint - no need to try to extract the values, and the message in - * this case looks different. - */ - if (riinfo->nkeys == 0) - { - ereport(ERROR, - (errcode(ERRCODE_FOREIGN_KEY_VIOLATION), - errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"", - RelationGetRelationName(fk_rel), - NameStr(riinfo->conname)), - errdetail("No rows were found in \"%s\".", - RelationGetRelationName(pk_rel)))); - } - /* Get printable versions of the keys involved */ initStringInfo(&key_names); initStringInfo(&key_values); -- 2.40.0