]> granicus.if.org Git - postgresql/commitdiff
Remove incomplete/incorrect support for zero-column foreign keys.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 21 Jun 2012 00:15:02 +0000 (20:15 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 21 Jun 2012 00:15:02 +0000 (20:15 -0400)
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

index c24396724f4411be78303bf5189a9c9d5c97c527..983f631234b8147e6d98e4819833eaa3dab85fd4 100644 (file)
@@ -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 <Table constraints>
-        *              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 <pktable>
-                        * ----------
-                        */
-                       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);