Fix up foreign-key mechanism so that there is a sound semantic basis for the

equality checks it applies, instead of a random dependence on whatever
operators might be named "=".  The equality operators will now be selected
from the opfamily of the unique index that the FK constraint depends on to
enforce uniqueness of the referenced columns; therefore they are certain to be
consistent with that index's notion of equality.  Among other things this
should fix the problem noted awhile back that pg_dump may fail for foreign-key
constraints on user-defined types when the required operators aren't in the
search path.  This also means that the former warning condition about "foreign
key constraint will require costly sequential scans" is gone: if the
comparison condition isn't indexable then we'll reject the constraint
entirely. All per past discussions.

Along the way, make the RI triggers look into pg_constraint for their
information, instead of using pg_trigger.tgargs; and get rid of the always
error-prone fixed-size string buffers in ri_triggers.c in favor of building up
the RI queries in StringInfo buffers.

initdb forced due to columns added to pg_constraint and pg_trigger.
This commit is contained in:
Tom Lane 2007-02-14 01:58:58 +00:00
parent 65e2f55031
commit 7bddca3450
33 changed files with 1731 additions and 1665 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.144 2007/01/31 20:56:16 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.145 2007/02/14 01:58:55 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
@ -1870,6 +1870,27 @@
<entry>If a foreign key, list of the referenced columns</entry>
</row>
<row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
</row>
<row>
<entry><structfield>conppeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = PK comparisons</entry>
</row>
<row>
<entry><structfield>conffeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for FK = FK comparisons</entry>
</row>
<row>
<entry><structfield>conbin</structfield></entry>
<entry><type>text</type></entry>
@ -1899,8 +1920,8 @@
<note>
<para>
<literal>pg_class.relchecks</literal> needs to agree with the
number of check-constraint entries found in this table for the
given relation.
number of check-constraint entries found in this table for each
relation.
</para>
</note>
@ -4166,35 +4187,42 @@
<entry><structfield>tgisconstraint</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>True if trigger implements a referential integrity constraint</entry>
<entry>True if trigger is a <quote>constraint trigger</></entry>
</row>
<row>
<entry><structfield>tgconstrname</structfield></entry>
<entry><type>name</type></entry>
<entry></entry>
<entry>Referential integrity constraint name</entry>
<entry>Constraint name, if a constraint trigger</entry>
</row>
<row>
<entry><structfield>tgconstrrelid</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
<entry>The table referenced by an referential integrity constraint</entry>
<entry>The table referenced by a referential integrity constraint</entry>
</row>
<row>
<entry><structfield>tgconstraint</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link>.oid</literal></entry>
<entry>The <structname>pg_constraint</> entry owning the trigger, if any</entry>
</row>
<row>
<entry><structfield>tgdeferrable</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>True if deferrable</entry>
<entry>True if constraint trigger is deferrable</entry>
</row>
<row>
<entry><structfield>tginitdeferred</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>True if initially deferred</entry>
<entry>True if constraint trigger is initially deferred</entry>
</row>
<row>
@ -4221,10 +4249,22 @@
</tgroup>
</table>
<note>
<para>
When <structfield>tgconstraint</> is nonzero,
<structfield>tgisconstraint</> must be true, and
<structfield>tgconstrname</>, <structfield>tgconstrrelid</>,
<structfield>tgdeferrable</>, <structfield>tginitdeferred</> are redundant
with the referenced <structname>pg_constraint</> entry. The reason we
keep these fields is that we support <quote>stand-alone</> constraint
triggers with no corresponding <structname>pg_constraint</> entry.
</para>
</note>
<note>
<para>
<literal>pg_class.reltriggers</literal> needs to agree with the
number of triggers found in this table for the given relation.
number of triggers found in this table for each relation.
</para>
</note>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.49 2007/02/01 00:28:18 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.50 2007/02/14 01:58:56 tgl Exp $ -->
<chapter id="triggers">
<title>Triggers</title>
@ -467,6 +467,7 @@ typedef struct Trigger
bool tgenabled;
bool tgisconstraint;
Oid tgconstrrelid;
Oid tgconstraint;
bool tgdeferrable;
bool tginitdeferred;
int16 tgnargs;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.136 2007/02/01 19:10:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.137 2007/02/14 01:58:56 tgl Exp $
*
* NOTES
* See acl.h.
@ -2314,7 +2314,7 @@ pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
if (superuser_arg(roleid))
return true;
tuple = SearchSysCache(CONOID,
tuple = SearchSysCache(CONVOID,
ObjectIdGetDatum(conv_oid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.63 2007/02/01 19:10:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.64 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1758,29 +1758,16 @@ getObjectDescription(const ObjectAddress *object)
case OCLASS_CONSTRAINT:
{
Relation conDesc;
ScanKeyData skey[1];
SysScanDesc rcscan;
HeapTuple tup;
HeapTuple conTup;
Form_pg_constraint con;
conDesc = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
SnapshotNow, 1, skey);
tup = systable_getnext(rcscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "could not find tuple for constraint %u",
conTup = SearchSysCache(CONSTROID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(conTup))
elog(ERROR, "cache lookup failed for constraint %u",
object->objectId);
con = (Form_pg_constraint) GETSTRUCT(tup);
con = (Form_pg_constraint) GETSTRUCT(conTup);
if (OidIsValid(con->conrelid))
{
@ -1794,8 +1781,7 @@ getObjectDescription(const ObjectAddress *object)
NameStr(con->conname));
}
systable_endscan(rcscan);
heap_close(conDesc, AccessShareLock);
ReleaseSysCache(conTup);
break;
}
@ -1803,7 +1789,7 @@ getObjectDescription(const ObjectAddress *object)
{
HeapTuple conTup;
conTup = SearchSysCache(CONOID,
conTup = SearchSysCache(CONVOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(conTup))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.316 2007/01/05 22:19:24 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.317 2007/02/14 01:58:56 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -1463,6 +1463,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
InvalidOid, /* not a domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
NULL,
NULL,
NULL,
0,
' ',
' ',

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.278 2007/02/01 19:10:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.279 2007/02/14 01:58:56 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -680,6 +680,9 @@ index_create(Oid heapRelationId,
InvalidOid, /* no domain */
InvalidOid, /* no foreign key */
NULL,
NULL,
NULL,
NULL,
0,
' ',
' ',

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.91 2007/02/01 19:10:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.92 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1199,7 +1199,7 @@ ConversionIsVisible(Oid conid)
Oid connamespace;
bool visible;
contup = SearchSysCache(CONOID,
contup = SearchSysCache(CONVOID,
ObjectIdGetDatum(conid),
0, 0, 0);
if (!HeapTupleIsValid(contup))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.34 2007/01/05 22:19:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.35 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,8 +19,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "utils/array.h"
@ -49,6 +48,9 @@ CreateConstraintEntry(const char *constraintName,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
@ -65,6 +67,9 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
NameData cname;
int i;
ObjectAddress conobject;
@ -92,16 +97,33 @@ CreateConstraintEntry(const char *constraintName,
if (foreignNKeys > 0)
{
Datum *confkey;
Datum *fkdatums;
confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
for (i = 0; i < foreignNKeys; i++)
confkey[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(confkey, foreignNKeys,
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
INT2OID, 2, true, 's');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
OIDOID, sizeof(Oid), true, 'i');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
conppeqopArray = construct_array(fkdatums, foreignNKeys,
OIDOID, sizeof(Oid), true, 'i');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
conffeqopArray = construct_array(fkdatums, foreignNKeys,
OIDOID, sizeof(Oid), true, 'i');
}
else
{
confkeyArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
}
/* initialize nulls and values */
for (i = 0; i < Natts_pg_constraint; i++)
@ -132,6 +154,21 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = 'n';
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
nulls[Anum_pg_constraint_conpfeqop - 1] = 'n';
if (conppeqopArray)
values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
else
nulls[Anum_pg_constraint_conppeqop - 1] = 'n';
if (conffeqopArray)
values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
else
nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
/*
* initialize the binary form of the check constraint.
*/
@ -246,6 +283,36 @@ CreateConstraintEntry(const char *constraintName,
recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
}
if (foreignNKeys > 0)
{
/*
* Register normal dependencies on the equality operators that
* support a foreign-key constraint. If the PK and FK types
* are the same then all three operators for a column are the
* same; otherwise they are different.
*/
ObjectAddress oprobject;
oprobject.classId = OperatorRelationId;
oprobject.objectSubId = 0;
for (i = 0; i < foreignNKeys; i++)
{
oprobject.objectId = pfEqOp[i];
recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
if (ppEqOp[i] != pfEqOp[i])
{
oprobject.objectId = ppEqOp[i];
recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
}
if (ffEqOp[i] != pfEqOp[i])
{
oprobject.objectId = ffEqOp[i];
recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
}
}
}
if (conExpr != NULL)
{
/*
@ -419,24 +486,16 @@ void
RemoveConstraintById(Oid conId)
{
Relation conDesc;
ScanKeyData skey[1];
SysScanDesc conscan;
HeapTuple tup;
Form_pg_constraint con;
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
ScanKeyInit(&skey[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conId));
conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
SnapshotNow, 1, skey);
tup = systable_getnext(conscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "could not find tuple for constraint %u", conId);
tup = SearchSysCache(CONSTROID,
ObjectIdGetDatum(conId),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for constraint %u", conId);
con = (Form_pg_constraint) GETSTRUCT(tup);
/*
@ -505,97 +564,10 @@ RemoveConstraintById(Oid conId)
simple_heap_delete(conDesc, &tup->t_self);
/* Clean up */
systable_endscan(conscan);
ReleaseSysCache(tup);
heap_close(conDesc, RowExclusiveLock);
}
/*
* GetConstraintNameForTrigger
* Get the name of the constraint owning a trigger, if any
*
* Returns a palloc'd string, or NULL if no constraint can be found
*/
char *
GetConstraintNameForTrigger(Oid triggerId)
{
char *result;
Oid constraintId = InvalidOid;
Relation depRel;
Relation conRel;
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
/*
* We must grovel through pg_depend to find the owning constraint. Perhaps
* pg_trigger should have a column for the owning constraint ... but right
* now this is not performance-critical code.
*/
depRel = heap_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(TriggerRelationId));
ScanKeyInit(&key[1],
Anum_pg_depend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(triggerId));
/* assume we can ignore objsubid for a trigger */
scan = systable_beginscan(depRel, DependDependerIndexId, true,
SnapshotNow, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
if (foundDep->refclassid == ConstraintRelationId &&
foundDep->deptype == DEPENDENCY_INTERNAL)
{
constraintId = foundDep->refobjid;
break;
}
}
systable_endscan(scan);
heap_close(depRel, AccessShareLock);
if (!OidIsValid(constraintId))
return NULL; /* no owning constraint found */
conRel = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&key[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(constraintId));
scan = systable_beginscan(conRel, ConstraintOidIndexId, true,
SnapshotNow, 1, key);
tup = systable_getnext(scan);
if (HeapTupleIsValid(tup))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
result = pstrdup(NameStr(con->conname));
}
else
{
/* This arguably should be an error, but we'll just return NULL */
result = NULL;
}
systable_endscan(scan);
heap_close(conRel, AccessShareLock);
return result;
}
/*
* AlterConstraintNamespaces
* Find any constraints belonging to the specified object,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.34 2007/01/05 22:19:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.35 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -146,7 +146,7 @@ ConversionDrop(Oid conversionOid, DropBehavior behavior)
HeapTuple tuple;
ObjectAddress object;
tuple = SearchSysCache(CONOID,
tuple = SearchSysCache(CONVOID,
ObjectIdGetDatum(conversionOid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
@ -313,7 +313,7 @@ pg_convert_using(PG_FUNCTION_ARGS)
errmsg("conversion \"%s\" does not exist",
NameListToString(parsed_name))));
tuple = SearchSysCache(CONOID,
tuple = SearchSysCache(CONVOID,
ObjectIdGetDatum(convoid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.30 2007/01/05 22:19:25 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.31 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -146,7 +146,7 @@ RenameConversion(List *name, const char *newname)
errmsg("conversion \"%s\" does not exist",
NameListToString(name))));
tup = SearchSysCacheCopy(CONOID,
tup = SearchSysCacheCopy(CONVOID,
ObjectIdGetDatum(conversionOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
@ -236,7 +236,7 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
Assert(RelationGetRelid(rel) == ConversionRelationId);
tup = SearchSysCacheCopy(CONOID,
tup = SearchSysCacheCopy(CONVOID,
ObjectIdGetDatum(conversionOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.153 2007/01/05 22:19:26 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.154 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -327,8 +327,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
if (instr->ntuples == 0)
continue;
if (trig->tgisconstraint &&
(conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)
if (OidIsValid(trig->tgconstraint) &&
(conname = get_constraint_name(trig->tgconstraint)) != NULL)
{
appendStringInfo(&buf, "Trigger for constraint %s",
conname);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.213 2007/02/02 00:07:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.214 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,6 +28,7 @@
#include "catalog/pg_depend.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/toasting.h"
@ -139,6 +140,7 @@ typedef struct NewConstraint
char *name; /* Constraint name, or NULL if none */
ConstrType contype; /* CHECK or FOREIGN */
Oid refrelid; /* PK rel, if FOREIGN */
Oid conid; /* OID of pg_constraint entry, if FOREIGN */
Node *qual; /* Check expr or FkConstraint struct */
List *qualstate; /* Execution state for CHECK */
} NewConstraint;
@ -186,10 +188,9 @@ static Oid transformFkeyCheckAttrs(Relation pkrel,
int numattrs, int16 *attnums,
Oid *opclasses);
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel, Relation pkrel);
Relation rel, Relation pkrel, Oid constraintOid);
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
Oid constrOid);
static char *fkMatchTypeToString(char match_type);
Oid constraintOid);
static void ATController(Relation rel, List *cmds, bool recurse);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing);
@ -256,11 +257,6 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
static void ATExecAddInherit(Relation rel, RangeVar *parent);
static void ATExecDropInherit(Relation rel, RangeVar *parent);
static void copy_relation_data(Relation rel, SMgrRelation dst);
static void update_ri_trigger_args(Oid relid,
const char *oldname,
const char *newname,
bool fk_scan,
bool update_relname);
/* ----------------------------------------------------------------
@ -1615,21 +1611,6 @@ renameatt(Oid myrelid,
heap_close(attrelation, RowExclusiveLock);
/*
* Update att name in any RI triggers associated with the relation.
*/
if (targetrelation->rd_rel->reltriggers > 0)
{
/* update tgargs column reference where att is primary key */
update_ri_trigger_args(RelationGetRelid(targetrelation),
oldattname, newattname,
false, false);
/* update tgargs column reference where att is foreign key */
update_ri_trigger_args(RelationGetRelid(targetrelation),
oldattname, newattname,
true, false);
}
relation_close(targetrelation, NoLock); /* close rel but keep lock */
}
@ -1708,226 +1689,12 @@ renamerel(Oid myrelid, const char *newrelname)
if (relkind != RELKIND_INDEX)
TypeRename(oldrelname, namespaceId, newrelname);
/*
* Update rel name in any RI triggers associated with the relation.
*/
if (relhastriggers)
{
/* update tgargs where relname is primary key */
update_ri_trigger_args(myrelid,
oldrelname,
newrelname,
false, true);
/* update tgargs where relname is foreign key */
update_ri_trigger_args(myrelid,
oldrelname,
newrelname,
true, true);
}
/*
* Close rel, but keep exclusive lock!
*/
relation_close(targetrelation, NoLock);
}
/*
* Scan pg_trigger for RI triggers that are on the specified relation
* (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
* is true). Update RI trigger args fields matching oldname to contain
* newname instead. If update_relname is true, examine the relname
* fields; otherwise examine the attname fields.
*/
static void
update_ri_trigger_args(Oid relid,
const char *oldname,
const char *newname,
bool fk_scan,
bool update_relname)
{
Relation tgrel;
ScanKeyData skey[1];
SysScanDesc trigscan;
HeapTuple tuple;
Datum values[Natts_pg_trigger];
char nulls[Natts_pg_trigger];
char replaces[Natts_pg_trigger];
tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
if (fk_scan)
{
ScanKeyInit(&skey[0],
Anum_pg_trigger_tgconstrrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
trigscan = systable_beginscan(tgrel, TriggerConstrRelidIndexId,
true, SnapshotNow,
1, skey);
}
else
{
ScanKeyInit(&skey[0],
Anum_pg_trigger_tgrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
trigscan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
true, SnapshotNow,
1, skey);
}
while ((tuple = systable_getnext(trigscan)) != NULL)
{
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
bytea *val;
bytea *newtgargs;
bool isnull;
int tg_type;
bool examine_pk;
bool changed;
int tgnargs;
int i;
int newlen;
const char *arga[RI_MAX_ARGUMENTS];
const char *argp;
tg_type = RI_FKey_trigger_type(pg_trigger->tgfoid);
if (tg_type == RI_TRIGGER_NONE)
{
/* Not an RI trigger, forget it */
continue;
}
/*
* It is an RI trigger, so parse the tgargs bytea.
*
* NB: we assume the field will never be compressed or moved out of
* line; so does trigger.c ...
*/
tgnargs = pg_trigger->tgnargs;
val = DatumGetByteaP(fastgetattr(tuple,
Anum_pg_trigger_tgargs,
tgrel->rd_att, &isnull));
if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
tgnargs > RI_MAX_ARGUMENTS)
{
/* This probably shouldn't happen, but ignore busted triggers */
continue;
}
argp = (const char *) VARDATA(val);
for (i = 0; i < tgnargs; i++)
{
arga[i] = argp;
argp += strlen(argp) + 1;
}
/*
* Figure out which item(s) to look at. If the trigger is primary-key
* type and attached to my rel, I should look at the PK fields; if it
* is foreign-key type and attached to my rel, I should look at the FK
* fields. But the opposite rule holds when examining triggers found
* by tgconstrrel search.
*/
examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
changed = false;
if (update_relname)
{
/* Change the relname if needed */
i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
if (strcmp(arga[i], oldname) == 0)
{
arga[i] = newname;
changed = true;
}
}
else
{
/* Change attname(s) if needed */
i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
for (; i < tgnargs; i += 2)
{
if (strcmp(arga[i], oldname) == 0)
{
arga[i] = newname;
changed = true;
}
}
}
if (!changed)
{
/* Don't need to update this tuple */
continue;
}
/*
* Construct modified tgargs bytea.
*/
newlen = VARHDRSZ;
for (i = 0; i < tgnargs; i++)
newlen += strlen(arga[i]) + 1;
newtgargs = (bytea *) palloc(newlen);
VARATT_SIZEP(newtgargs) = newlen;
newlen = VARHDRSZ;
for (i = 0; i < tgnargs; i++)
{
strcpy(((char *) newtgargs) + newlen, arga[i]);
newlen += strlen(arga[i]) + 1;
}
/*
* Build modified tuple.
*/
for (i = 0; i < Natts_pg_trigger; i++)
{
values[i] = (Datum) 0;
replaces[i] = ' ';
nulls[i] = ' ';
}
values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
replaces[Anum_pg_trigger_tgargs - 1] = 'r';
tuple = heap_modifytuple(tuple, RelationGetDescr(tgrel), values, nulls, replaces);
/*
* Update pg_trigger and its indexes
*/
simple_heap_update(tgrel, &tuple->t_self, tuple);
CatalogUpdateIndexes(tgrel, tuple);
/*
* Invalidate trigger's relation's relcache entry so that other
* backends (and this one too!) are sent SI message to make them
* rebuild relcache entries. (Ideally this should happen
* automatically...)
*
* We can skip this for triggers on relid itself, since that relcache
* flush will happen anyway due to the table or column rename. We
* just need to catch the far ends of RI relationships.
*/
pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
if (pg_trigger->tgrelid != relid)
CacheInvalidateRelcacheByRelid(pg_trigger->tgrelid);
/* free up our scratch memory */
pfree(newtgargs);
heap_freetuple(tuple);
}
systable_endscan(trigscan);
heap_close(tgrel, RowExclusiveLock);
/*
* Increment cmd counter to make updates visible; this is needed in case
* the same tuple has to be updated again by next pass (can happen in case
* of a self-referential FK relationship).
*/
CommandCounterIncrement();
}
/*
* AlterTable
* Execute ALTER TABLE, which can be a list of subcommands
@ -2552,7 +2319,8 @@ ATRewriteTables(List **wqueue)
refrel = heap_open(con->refrelid, RowShareLock);
validateForeignKeyConstraint(fkconstraint, rel, refrel);
validateForeignKeyConstraint(fkconstraint, rel, refrel,
con->conid);
heap_close(refrel, NoLock);
}
@ -4061,6 +3829,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
Oid pfeqoperators[INDEX_MAX_KEYS];
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
int numfks,
numpks;
@ -4138,6 +3909,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
numfks = transformColumnNameList(RelationGetRelid(rel),
fkconstraint->fk_attrs,
@ -4166,7 +3940,15 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
opclasses);
}
/* Be sure referencing and referenced column types are comparable */
/*
* Look up the equality operators to use in the constraint.
*
* Note that we look for operator(s) with the PK type on the left; when
* the types are different this is the right choice because the PK index
* will need operators with the indexkey on the left. Also, we take the
* PK type as being the declared input type of the opclass, which might be
* binary-compatible but not identical to the PK column type.
*/
if (numfks != numpks)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
@ -4174,24 +3956,71 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
for (i = 0; i < numpks; i++)
{
/*
* pktypoid[i] is the primary key table's i'th key's type fktypoid[i]
* is the foreign key table's i'th key's type
*
* Note that we look for an operator with the PK type on the left;
* when the types are different this is critical because the PK index
* will need operators with the indexkey on the left. (Ordinarily both
* commutator operators will exist if either does, but we won't get
* the right answer from the test below on opclass membership unless
* we select the proper operator.)
*/
Operator o = oper(NULL, list_make1(makeString("=")),
pktypoid[i], fktypoid[i],
true, -1);
HeapTuple cla_ht;
Form_pg_opclass cla_tup;
Oid amid;
Oid opfamily;
Oid pktype;
Oid fktype;
Oid pfeqop;
Oid ppeqop;
Oid ffeqop;
int16 eqstrategy;
if (o == NULL)
/* We need several fields out of the pg_opclass entry */
cla_ht = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclasses[i]),
0, 0, 0);
if (!HeapTupleIsValid(cla_ht))
elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
amid = cla_tup->opcmethod;
opfamily = cla_tup->opcfamily;
pktype = cla_tup->opcintype;
ReleaseSysCache(cla_ht);
/*
* Check it's a btree; currently this can never fail since no other
* index AMs support unique indexes. If we ever did have other
* types of unique indexes, we'd need a way to determine which
* operator strategy number is equality. (Is it reasonable to
* insist that every such index AM use btree's number for equality?)
*/
if (amid != BTREE_AM_OID)
elog(ERROR, "only b-tree indexes are supported for foreign keys");
eqstrategy = BTEqualStrategyNumber;
/*
* There had better be a PK = PK operator for the index.
*/
ppeqop = get_opfamily_member(opfamily, pktype, pktype, eqstrategy);
if (!OidIsValid(ppeqop))
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
eqstrategy, pktype, pktype, opfamily);
/*
* Are there equality operators that take exactly the FK type?
* Assume we should look through any domain here.
*/
fktype = getBaseType(fktypoid[i]);
pfeqop = get_opfamily_member(opfamily, pktype, fktype, eqstrategy);
ffeqop = get_opfamily_member(opfamily, fktype, fktype, eqstrategy);
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
{
/*
* Otherwise, look for an implicit cast from the FK type to
* the PK type, and if found, use the PK type's equality operator.
*/
if (can_coerce_type(1, &fktype, &pktype, COERCION_IMPLICIT))
pfeqop = ffeqop = ppeqop;
}
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("foreign key constraint \"%s\" "
"cannot be implemented",
fkconstraint->constr_name),
@ -4202,41 +4031,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
format_type_be(fktypoid[i]),
format_type_be(pktypoid[i]))));
/*
* Check that the found operator is compatible with the PK index, and
* generate a warning if not, since otherwise costly seqscans will be
* incurred to check FK validity.
*/
if (!op_in_opfamily(oprid(o), get_opclass_family(opclasses[i])))
ereport(WARNING,
(errmsg("foreign key constraint \"%s\" "
"will require costly sequential scans",
fkconstraint->constr_name),
errdetail("Key columns \"%s\" and \"%s\" "
"are of different types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
format_type_be(fktypoid[i]),
format_type_be(pktypoid[i]))));
ReleaseSysCache(o);
}
/*
* Tell Phase 3 to check that the constraint is satisfied by existing rows
* (we can skip this during table creation).
*/
if (!fkconstraint->skip_validation)
{
NewConstraint *newcon;
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
newcon->name = fkconstraint->constr_name;
newcon->contype = CONSTR_FOREIGN;
newcon->refrelid = RelationGetRelid(pkrel);
newcon->qual = (Node *) fkconstraint;
tab->constraints = lappend(tab->constraints, newcon);
pfeqoperators[i] = pfeqop;
ppeqoperators[i] = ppeqop;
ffeqoperators[i] = ffeqop;
}
/*
@ -4254,6 +4051,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* constraint */
RelationGetRelid(pkrel),
pkattnum,
pfeqoperators,
ppeqoperators,
ffeqoperators,
numpks,
fkconstraint->fk_upd_action,
fkconstraint->fk_del_action,
@ -4268,6 +4068,24 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
createForeignKeyTriggers(rel, fkconstraint, constrOid);
/*
* Tell Phase 3 to check that the constraint is satisfied by existing rows
* (we can skip this during table creation).
*/
if (!fkconstraint->skip_validation)
{
NewConstraint *newcon;
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
newcon->name = fkconstraint->constr_name;
newcon->contype = CONSTR_FOREIGN;
newcon->refrelid = RelationGetRelid(pkrel);
newcon->conid = constrOid;
newcon->qual = (Node *) fkconstraint;
tab->constraints = lappend(tab->constraints, newcon);
}
/*
* Close pk table, but keep lock until we've committed.
*/
@ -4526,25 +4344,15 @@ transformFkeyCheckAttrs(Relation pkrel,
static void
validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel,
Relation pkrel)
Relation pkrel,
Oid constraintOid)
{
HeapScanDesc scan;
HeapTuple tuple;
Trigger trig;
ListCell *list;
int count;
/*
* See if we can do it with a single LEFT JOIN query. A FALSE result
* indicates we must proceed with the fire-the-trigger method.
*/
if (RI_Initial_Check(fkconstraint, rel, pkrel))
return;
/*
* Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
* if that tuple had just been inserted. If any of those fail, it should
* ereport(ERROR) and that's that.
* Build a trigger call structure; we'll need it either way.
*/
MemSet(&trig, 0, sizeof(trig));
trig.tgoid = InvalidOid;
@ -4552,35 +4360,23 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
trig.tgenabled = TRUE;
trig.tgisconstraint = TRUE;
trig.tgconstrrelid = RelationGetRelid(pkrel);
trig.tgconstraint = constraintOid;
trig.tgdeferrable = FALSE;
trig.tginitdeferred = FALSE;
/* we needn't fill in tgargs */
trig.tgargs = (char **) palloc(sizeof(char *) *
(4 + list_length(fkconstraint->fk_attrs)
+ list_length(fkconstraint->pk_attrs)));
trig.tgargs[0] = trig.tgname;
trig.tgargs[1] = RelationGetRelationName(rel);
trig.tgargs[2] = RelationGetRelationName(pkrel);
trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype);
count = 4;
foreach(list, fkconstraint->fk_attrs)
{
char *fk_at = strVal(lfirst(list));
trig.tgargs[count] = fk_at;
count += 2;
}
count = 5;
foreach(list, fkconstraint->pk_attrs)
{
char *pk_at = strVal(lfirst(list));
trig.tgargs[count] = pk_at;
count += 2;
}
trig.tgnargs = count - 1;
/*
* See if we can do it with a single LEFT JOIN query. A FALSE result
* indicates we must proceed with the fire-the-trigger method.
*/
if (RI_Initial_Check(&trig, rel, pkrel))
return;
/*
* Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
* if that tuple had just been inserted. If any of those fail, it should
* ereport(ERROR) and that's that.
*/
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
@ -4613,18 +4409,13 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
}
heap_endscan(scan);
pfree(trig.tgargs);
}
static void
CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
ObjectAddress *constrobj, ObjectAddress *trigobj,
bool on_insert)
Oid constraintOid, bool on_insert)
{
CreateTrigStmt *fk_trigger;
ListCell *fk_attr;
ListCell *pk_attr;
fk_trigger = makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
@ -4649,32 +4440,9 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = fkconstraint->pktable;
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString(myRel->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
if (list_length(fkconstraint->fk_attrs) != list_length(fkconstraint->pk_attrs))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
errmsg("number of referencing and referenced columns for foreign key disagree")));
forboth(fk_attr, fkconstraint->fk_attrs,
pk_attr, fkconstraint->pk_attrs)
{
fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr));
fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr));
}
trigobj->objectId = CreateTrigger(fk_trigger, true);
/* Register dependency from trigger to constraint */
recordDependencyOn(trigobj, constrobj, DEPENDENCY_INTERNAL);
(void) CreateTrigger(fk_trigger, constraintOid);
/* Make changes-so-far visible */
CommandCounterIncrement();
@ -4685,14 +4453,10 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
*/
static void
createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
Oid constrOid)
Oid constraintOid)
{
RangeVar *myRel;
CreateTrigStmt *fk_trigger;
ListCell *fk_attr;
ListCell *pk_attr;
ObjectAddress trigobj,
constrobj;
/*
* Reconstruct a RangeVar for my relation (not passed in, unfortunately).
@ -4700,15 +4464,6 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
pstrdup(RelationGetRelationName(rel)));
/*
* Preset objectAddress fields
*/
constrobj.classId = ConstraintRelationId;
constrobj.objectId = constrOid;
constrobj.objectSubId = 0;
trigobj.classId = TriggerRelationId;
trigobj.objectSubId = 0;
/* Make changes-so-far visible */
CommandCounterIncrement();
@ -4716,8 +4471,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK
* action for both INSERTs and UPDATEs on the referencing table.
*/
CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, true);
CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, false);
CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, true);
CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, false);
/*
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
@ -4765,27 +4520,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
(int) fkconstraint->fk_del_action);
break;
}
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString(myRel->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
forboth(fk_attr, fkconstraint->fk_attrs,
pk_attr, fkconstraint->pk_attrs)
{
fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr));
fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr));
}
trigobj.objectId = CreateTrigger(fk_trigger, true);
/* Register dependency from trigger to constraint */
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
(void) CreateTrigger(fk_trigger, constraintOid);
/* Make changes-so-far visible */
CommandCounterIncrement();
@ -4835,49 +4572,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
(int) fkconstraint->fk_upd_action);
break;
}
fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->constr_name));
fk_trigger->args = lappend(fk_trigger->args,
makeString(myRel->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->pktable->relname));
fk_trigger->args = lappend(fk_trigger->args,
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
forboth(fk_attr, fkconstraint->fk_attrs,
pk_attr, fkconstraint->pk_attrs)
{
fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr));
fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr));
}
trigobj.objectId = CreateTrigger(fk_trigger, true);
/* Register dependency from trigger to constraint */
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
}
/*
* fkMatchTypeToString -
* convert FKCONSTR_MATCH_xxx code to string to use in trigger args
*/
static char *
fkMatchTypeToString(char match_type)
{
switch (match_type)
{
case FKCONSTR_MATCH_FULL:
return pstrdup("FULL");
case FKCONSTR_MATCH_PARTIAL:
return pstrdup("PARTIAL");
case FKCONSTR_MATCH_UNSPECIFIED:
return pstrdup("UNSPECIFIED");
default:
elog(ERROR, "unrecognized match type: %d",
(int) match_type);
}
return NULL; /* can't get here */
(void) CreateTrigger(fk_trigger, constraintOid);
}
/*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.212 2007/01/25 04:17:46 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.213 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@ -57,13 +58,12 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
/*
* Create a trigger. Returns the OID of the created trigger.
*
* forConstraint, if true, says that this trigger is being created to
* implement a constraint. The caller will then be expected to make
* a pg_depend entry linking the trigger to that constraint (and thereby
* to the owning relation(s)).
* constraintOid, if nonzero, says that this trigger is being created to
* implement that constraint. A suitable pg_depend entry will be made
* to link the trigger to that constraint.
*/
Oid
CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
{
int16 tgtype;
int2vector *tgattr;
@ -91,51 +91,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
if (stmt->constrrel != NULL)
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
else if (stmt->isconstraint)
{
/*
* If this trigger is a constraint (and a foreign key one) then we
* really need a constrrelid. Since we don't have one, we'll try to
* generate one from the argument information.
*
* This is really just a workaround for a long-ago pg_dump bug that
* omitted the FROM clause in dumped CREATE CONSTRAINT TRIGGER
* commands. We don't want to bomb out completely here if we can't
* determine the correct relation, because that would prevent loading
* the dump file. Instead, NOTICE here and ERROR in the trigger.
*/
bool needconstrrelid = false;
void *elem = NULL;
if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_check_", 14) == 0)
{
/* A trigger on FK table. */
needconstrrelid = true;
if (list_length(stmt->args) > RI_PK_RELNAME_ARGNO)
elem = list_nth(stmt->args, RI_PK_RELNAME_ARGNO);
}
else if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_", 8) == 0)
{
/* A trigger on PK table. */
needconstrrelid = true;
if (list_length(stmt->args) > RI_FK_RELNAME_ARGNO)
elem = list_nth(stmt->args, RI_FK_RELNAME_ARGNO);
}
if (elem != NULL)
{
RangeVar *rel = makeRangeVar(NULL, strVal(elem));
constrrelid = RangeVarGetRelid(rel, true);
}
if (needconstrrelid && constrrelid == InvalidOid)
ereport(NOTICE,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("could not determine referenced table for constraint \"%s\"",
stmt->trigname)));
}
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@ -152,15 +107,17 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
if (stmt->isconstraint)
{
/* foreign key constraint trigger */
/* constraint trigger */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
ACL_REFERENCES);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
if (constrrelid != InvalidOid)
if (stmt->constrrel != NULL)
{
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
ACL_REFERENCES);
if (aclresult != ACLCHECK_OK)
@ -170,7 +127,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
}
else
{
/* real trigger */
/* regular trigger */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
ACL_TRIGGER);
if (aclresult != ACLCHECK_OK)
@ -187,23 +144,31 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
trigoid = GetNewOid(tgrel);
/*
* If trigger is an RI constraint, use specified trigger name as
* constraint name and build a unique trigger name instead. This is mainly
* for backwards compatibility with CREATE CONSTRAINT TRIGGER commands.
* If trigger is for an RI constraint, the passed-in name is the
* constraint name; save that and build a unique trigger name to avoid
* collisions with user-selected trigger names.
*/
if (stmt->isconstraint)
if (OidIsValid(constraintOid))
{
snprintf(constrtrigname, sizeof(constrtrigname),
"RI_ConstraintTrigger_%u", trigoid);
trigname = constrtrigname;
constrname = stmt->trigname;
}
else if (stmt->isconstraint)
{
/* constraint trigger: trigger name is also constraint name */
trigname = stmt->trigname;
constrname = stmt->trigname;
}
else
{
/* regular trigger: use empty constraint name */
trigname = stmt->trigname;
constrname = "";
}
/* Compute tgtype */
TRIGGER_CLEAR_TYPE(tgtype);
if (stmt->before)
TRIGGER_SETT_BEFORE(tgtype);
@ -298,7 +263,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
/*
* Build the new pg_trigger tuple.
*/
MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
memset(nulls, ' ', Natts_pg_trigger * sizeof(char));
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
@ -310,6 +275,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
CStringGetDatum(constrname));
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
@ -372,10 +338,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
CatalogUpdateIndexes(tgrel, tuple);
myself.classId = TriggerRelationId;
myself.objectId = trigoid;
myself.objectSubId = 0;
heap_freetuple(tuple);
heap_close(tgrel, RowExclusiveLock);
@ -412,20 +374,36 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
/*
* Record dependencies for trigger. Always place a normal dependency on
* the function. If we are doing this in response to an explicit CREATE
* TRIGGER command, also make trigger be auto-dropped if its relation is
* dropped or if the FK relation is dropped. (Auto drop is compatible
* with our pre-7.3 behavior.) If the trigger is being made for a
* constraint, we can skip the relation links; the dependency on the
* constraint will indirectly depend on the relations.
* the function.
*/
myself.classId = TriggerRelationId;
myself.objectId = trigoid;
myself.objectSubId = 0;
referenced.classId = ProcedureRelationId;
referenced.objectId = funcoid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
if (!forConstraint)
if (OidIsValid(constraintOid))
{
/*
* It's for a constraint, so make it an internal dependency of the
* constraint. We can skip depending on the relations, as there'll
* be an indirect dependency via the constraint.
*/
referenced.classId = ConstraintRelationId;
referenced.objectId = constraintOid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
}
else
{
/*
* Regular CREATE TRIGGER, so place dependencies. We make trigger be
* auto-dropped if its relation is dropped or if the FK relation is
* dropped. (Auto drop is compatible with our pre-7.3 behavior.)
*/
referenced.classId = RelationRelationId;
referenced.objectId = RelationGetRelid(rel);
referenced.objectSubId = 0;
@ -773,7 +751,7 @@ EnableDisableTrigger(Relation rel, const char *tgname,
{
Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
if (oldtrig->tgisconstraint)
if (OidIsValid(oldtrig->tgconstraint))
{
/* system trigger ... ok to process? */
if (skip_system)
@ -886,6 +864,7 @@ RelationBuildTriggers(Relation relation)
build->tgenabled = pg_trigger->tgenabled;
build->tgisconstraint = pg_trigger->tgisconstraint;
build->tgconstrrelid = pg_trigger->tgconstrrelid;
build->tgconstraint = pg_trigger->tgconstraint;
build->tgdeferrable = pg_trigger->tgdeferrable;
build->tginitdeferred = pg_trigger->tginitdeferred;
build->tgnargs = pg_trigger->tgnargs;
@ -1220,6 +1199,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
return false;
if (trig1->tgconstrrelid != trig2->tgconstrrelid)
return false;
if (trig1->tgconstraint != trig2->tgconstraint)
return false;
if (trig1->tgdeferrable != trig2->tgdeferrable)
return false;
if (trig1->tginitdeferred != trig2->tginitdeferred)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.99 2007/01/05 22:19:26 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.100 2007/02/14 01:58:57 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -1966,6 +1966,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
domainOid, /* domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
NULL,
NULL,
NULL,
0,
' ',
' ',

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.271 2007/01/23 05:07:18 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.272 2007/02/14 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -973,7 +973,7 @@ ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
CreateTrigger((CreateTrigStmt *) parsetree, false);
CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid);
break;
case T_DropPropertyStmt:

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.248 2007/02/03 14:06:54 petere Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.249 2007/02/14 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -875,30 +875,15 @@ static char *
pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags)
{
StringInfoData buf;
Relation conDesc;
SysScanDesc conscan;
ScanKeyData skey[1];
HeapTuple tup;
Form_pg_constraint conForm;
StringInfoData buf;
/*
* Fetch the pg_constraint row. There's no syscache for pg_constraint so
* we must do it the hard way.
*/
conDesc = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(constraintId));
conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
SnapshotNow, 1, skey);
tup = systable_getnext(conscan);
if (!HeapTupleIsValid(tup))
elog(ERROR, "could not find tuple for constraint %u", constraintId);
tup = SearchSysCache(CONSTROID,
ObjectIdGetDatum(constraintId),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for constraint %u", constraintId);
conForm = (Form_pg_constraint) GETSTRUCT(tup);
initStringInfo(&buf);
@ -922,8 +907,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfo(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
val = heap_getattr(tup, Anum_pg_constraint_conkey,
RelationGetDescr(conDesc), &isnull);
val = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conkey, &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
@ -935,8 +920,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
generate_relation_name(conForm->confrelid));
/* Fetch and build referenced-column list */
val = heap_getattr(tup, Anum_pg_constraint_confkey,
RelationGetDescr(conDesc), &isnull);
val = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_confkey, &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
@ -1038,8 +1023,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfo(&buf, "UNIQUE (");
/* Fetch and build target column list */
val = heap_getattr(tup, Anum_pg_constraint_conkey,
RelationGetDescr(conDesc), &isnull);
val = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conkey, &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
@ -1071,8 +1056,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
List *context;
/* Fetch constraint expression in parsetree form */
val = heap_getattr(tup, Anum_pg_constraint_conbin,
RelationGetDescr(conDesc), &isnull);
val = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conbin, &isnull);
if (isnull)
elog(ERROR, "null conbin for constraint %u",
constraintId);
@ -1115,8 +1100,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
}
/* Cleanup */
systable_endscan(conscan);
heap_close(conDesc, AccessShareLock);
ReleaseSysCache(tup);
return buf.data;
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.147 2007/01/30 01:33:36 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.148 2007/02/14 01:58:57 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -20,6 +20,7 @@
#include "bootstrap/bootstrap.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
@ -897,10 +898,37 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
ReleaseSysCache(tp);
}
/* ---------- INDEX CACHE ---------- */
/* ---------- CONSTRAINT CACHE ---------- */
/* watch this space...
/*
* get_constraint_name
* Returns the name of a given pg_constraint entry.
*
* Returns a palloc'd copy of the string, or NULL if no such constraint.
*
* NOTE: since constraint name is not unique, be wary of code that uses this
* for anything except preparing error messages.
*/
char *
get_constraint_name(Oid conoid)
{
HeapTuple tp;
tp = SearchSysCache(CONSTROID,
ObjectIdGetDatum(conoid),
0, 0, 0);
if (HeapTupleIsValid(tp))
{
Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
char *result;
result = pstrdup(NameStr(contup->conname));
ReleaseSysCache(tp);
return result;
}
else
return NULL;
}
/* ---------- OPCLASS CACHE ---------- */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.110 2007/01/05 22:19:43 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.111 2007/02/14 01:58:57 tgl Exp $
*
* NOTES
* These routines allow the parser/planner/executor to perform
@ -28,6 +28,7 @@
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_language.h"
@ -298,7 +299,19 @@ static const struct cachedesc cacheinfo[] = {
},
128
},
{ConversionRelationId, /* CONOID */
{ConstraintRelationId, /* CONSTROID */
ConstraintOidIndexId,
0,
1,
{
ObjectIdAttributeNumber,
0,
0,
0
},
1024
},
{ConversionRelationId, /* CONVOID */
ConversionOidIndexId,
0,
1,

View File

@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.459 2007/01/25 03:30:43 momjian Exp $
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.460 2007/02/14 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -3841,11 +3841,30 @@ getTriggers(TableInfo tblinfo[], int numTables)
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
resetPQExpBuffer(query);
if (g_fout->remoteVersion >= 70300)
if (g_fout->remoteVersion >= 80300)
{
/*
* We ignore triggers that are tied to a foreign-key constraint
*/
appendPQExpBuffer(query,
"SELECT tgname, "
"tgfoid::pg_catalog.regproc as tgfname, "
"tgtype, tgnargs, tgargs, tgenabled, "
"tgisconstraint, tgconstrname, tgdeferrable, "
"tgconstrrelid, tginitdeferred, tableoid, oid, "
"tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
"from pg_catalog.pg_trigger t "
"where tgrelid = '%u'::pg_catalog.oid "
"and tgconstraint = 0",
tbinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70300)
{
/*
* We ignore triggers that are tied to a foreign-key constraint,
* but in these versions we have to grovel through pg_constraint
* to find out
*/
appendPQExpBuffer(query,
"SELECT tgname, "
"tgfoid::pg_catalog.regproc as tgfname, "

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.150 2007/01/20 21:17:30 neilc Exp $
* $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.151 2007/02/14 01:58:58 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
@ -1128,12 +1128,8 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' "
"AND t.tgenabled "
"AND (NOT t.tgisconstraint "
" OR NOT EXISTS"
" (SELECT 1 FROM pg_catalog.pg_depend d "
" JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
" WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))"
" ORDER BY 1",
"AND t.tgconstraint = 0\n"
"ORDER BY 1",
oid);
result4 = PSQLexec(buf.data, false);
if (!result4)
@ -1152,12 +1148,8 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' "
"AND NOT t.tgenabled "
"AND (NOT t.tgisconstraint "
" OR NOT EXISTS"
" (SELECT 1 FROM pg_catalog.pg_depend d "
" JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
" WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))"
" ORDER BY 1",
"AND t.tgconstraint = 0\n"
"ORDER BY 1",
oid);
result7 = PSQLexec(buf.data, false);
if (!result7)

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.383 2007/02/09 03:35:34 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.384 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200702081
#define CATALOG_VERSION_NO 200702131
#endif

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.97 2007/01/05 22:19:52 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.98 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -214,9 +214,6 @@ DECLARE_UNIQUE_INDEX(pg_tablespace_spcname_index, 2698, on pg_tablespace using b
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_trigger_tgconstrname_index, 2699, on pg_trigger using btree(tgconstrname name_ops));
#define TriggerConstrNameIndexId 2699
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_trigger_tgconstrrelid_index, 2700, on pg_trigger using btree(tgconstrrelid oid_ops));
#define TriggerConstrRelidIndexId 2700
DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using btree(tgrelid oid_ops, tgname name_ops));
#define TriggerRelidNameIndexId 2701
DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.24 2007/01/05 22:19:52 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.25 2007/02/14 01:58:58 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -91,6 +91,24 @@ CATALOG(pg_constraint,2606)
*/
int2 confkey[1];
/*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
*/
Oid conpfeqop[1];
/*
* If a foreign key, the OIDs of the PK = PK equality operators for each
* column of the constraint (i.e., equality for the referenced columns)
*/
Oid conppeqop[1];
/*
* If a foreign key, the OIDs of the FK = FK equality operators for each
* column of the constraint (i.e., equality for the referencing columns)
*/
Oid conffeqop[1];
/*
* If a check constraint, nodeToString representation of expression
*/
@ -113,7 +131,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
#define Natts_pg_constraint 15
#define Natts_pg_constraint 18
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@ -127,8 +145,11 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_confmatchtype 11
#define Anum_pg_constraint_conkey 12
#define Anum_pg_constraint_confkey 13
#define Anum_pg_constraint_conbin 14
#define Anum_pg_constraint_consrc 15
#define Anum_pg_constraint_conpfeqop 14
#define Anum_pg_constraint_conppeqop 15
#define Anum_pg_constraint_conffeqop 16
#define Anum_pg_constraint_conbin 17
#define Anum_pg_constraint_consrc 18
/* Valid values for contype */
@ -167,6 +188,9 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
@ -184,8 +208,6 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
const char *label, Oid namespace,
List *others);
extern char *GetConstraintNameForTrigger(Oid triggerId);
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType);

View File

@ -1,11 +1,14 @@
/*-------------------------------------------------------------------------
*
* pg_trigger.h
* definition of the system "trigger" relation (pg_trigger)
* along with the relation's initial contents.
*
*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.26 2007/01/05 22:19:53 momjian Exp $
*
* $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.27 2007/02/14 01:58:58 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -26,23 +29,30 @@
/* ----------------
* pg_trigger definition. cpp turns this into
* typedef struct FormData_pg_trigger
*
* Note: when tgconstraint is nonzero, tgisconstraint must be true, and
* tgconstrname, tgconstrrelid, tgdeferrable, tginitdeferred are redundant
* with the referenced pg_constraint entry. The reason we keep these fields
* is that we support "stand-alone" constraint triggers with no corresponding
* pg_constraint entry.
* ----------------
*/
#define TriggerRelationId 2620
CATALOG(pg_trigger,2620)
{
Oid tgrelid; /* triggered relation */
NameData tgname; /* trigger' name */
Oid tgrelid; /* relation trigger is attached to */
NameData tgname; /* trigger's name */
Oid tgfoid; /* OID of function to be called */
int2 tgtype; /* BEFORE/AFTER UPDATE/DELETE/INSERT
* ROW/STATEMENT */
* ROW/STATEMENT; see below */
bool tgenabled; /* trigger is enabled/disabled */
bool tgisconstraint; /* trigger is a RI constraint */
NameData tgconstrname; /* RI constraint name */
Oid tgconstrrelid; /* RI table of foreign key definition */
bool tgdeferrable; /* RI trigger is deferrable */
bool tginitdeferred; /* RI trigger is deferred initially */
bool tgisconstraint; /* trigger is a constraint trigger */
NameData tgconstrname; /* constraint name */
Oid tgconstrrelid; /* constraint's FROM table, if any */
Oid tgconstraint; /* owning pg_constraint entry, if any */
bool tgdeferrable; /* constraint trigger is deferrable */
bool tginitdeferred; /* constraint trigger is deferred initially */
int2 tgnargs; /* # of extra arguments in tgargs */
/* VARIABLE LENGTH FIELDS: */
@ -61,7 +71,7 @@ typedef FormData_pg_trigger *Form_pg_trigger;
* compiler constants for pg_trigger
* ----------------
*/
#define Natts_pg_trigger 13
#define Natts_pg_trigger 14
#define Anum_pg_trigger_tgrelid 1
#define Anum_pg_trigger_tgname 2
#define Anum_pg_trigger_tgfoid 3
@ -70,18 +80,21 @@ typedef FormData_pg_trigger *Form_pg_trigger;
#define Anum_pg_trigger_tgisconstraint 6
#define Anum_pg_trigger_tgconstrname 7
#define Anum_pg_trigger_tgconstrrelid 8
#define Anum_pg_trigger_tgdeferrable 9
#define Anum_pg_trigger_tginitdeferred 10
#define Anum_pg_trigger_tgnargs 11
#define Anum_pg_trigger_tgattr 12
#define Anum_pg_trigger_tgargs 13
#define Anum_pg_trigger_tgconstraint 9
#define Anum_pg_trigger_tgdeferrable 10
#define Anum_pg_trigger_tginitdeferred 11
#define Anum_pg_trigger_tgnargs 12
#define Anum_pg_trigger_tgattr 13
#define Anum_pg_trigger_tgargs 14
/* Bits within tgtype */
#define TRIGGER_TYPE_ROW (1 << 0)
#define TRIGGER_TYPE_BEFORE (1 << 1)
#define TRIGGER_TYPE_INSERT (1 << 2)
#define TRIGGER_TYPE_DELETE (1 << 3)
#define TRIGGER_TYPE_UPDATE (1 << 4)
/* Macros for manipulating tgtype */
#define TRIGGER_CLEAR_TYPE(type) ((type) = 0)
#define TRIGGER_SETT_ROW(type) ((type) |= TRIGGER_TYPE_ROW)

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.60 2007/01/05 22:19:54 momjian Exp $
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.61 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -78,34 +78,8 @@ typedef struct TriggerData
#define TRIGGER_FIRED_AFTER(event) \
(!TRIGGER_FIRED_BEFORE (event))
/*
* RI trigger function arguments are stored in pg_trigger.tgargs bytea
*
* constrname\0fkrel\0pkrel\0matchtype\0fkatt\0pkatt\0fkatt\0pkatt\0...
*
* There are one or more pairs of fkatt/pkatt names.
*
* The relation names are no longer of much use since they are not
* guaranteed unique; they are present only for backwards compatibility.
* Use the tgrelid and tgconstrrelid fields to identify the referenced
* relations, instead. (But note that which is which will depend on which
* trigger you are looking at!)
*/
#define RI_CONSTRAINT_NAME_ARGNO 0
#define RI_FK_RELNAME_ARGNO 1
#define RI_PK_RELNAME_ARGNO 2
#define RI_MATCH_TYPE_ARGNO 3
#define RI_FIRST_ATTNAME_ARGNO 4 /* first attname pair starts
* here */
#define RI_KEYPAIR_FK_IDX 0
#define RI_KEYPAIR_PK_IDX 1
#define RI_MAX_NUMKEYS INDEX_MAX_KEYS
#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
extern Oid CreateTrigger(CreateTrigStmt *stmt, bool forConstraint);
extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid);
extern void DropTrigger(Oid relid, const char *trigname,
DropBehavior behavior, bool missing_ok);
@ -175,10 +149,10 @@ extern bool RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
HeapTuple old_row, HeapTuple new_row);
extern bool RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
HeapTuple old_row, HeapTuple new_row);
extern bool RI_Initial_Check(FkConstraint *fkconstraint,
Relation rel,
Relation pkrel);
extern bool RI_Initial_Check(Trigger *trigger,
Relation fk_rel, Relation pk_rel);
/* result values for RI_FKey_trigger_type: */
#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */
#define RI_TRIGGER_FK 2 /* is a trigger on the FK relation */
#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.116 2007/01/30 01:33:36 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.117 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -58,6 +58,7 @@ extern Oid get_atttype(Oid relid, AttrNumber attnum);
extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
Oid *typid, int32 *typmod);
extern char *get_constraint_name(Oid conoid);
extern Oid get_opclass_family(Oid opclass);
extern Oid get_opclass_input_type(Oid opclass);
extern RegProcedure get_opcode(Oid opno);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.96 2007/01/25 02:17:26 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.97 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,6 +56,7 @@ typedef struct Trigger
bool tgenabled;
bool tgisconstraint;
Oid tgconstrrelid;
Oid tgconstraint;
bool tgdeferrable;
bool tginitdeferred;
int16 tgnargs;

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.67 2007/01/05 22:19:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.68 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,42 +28,43 @@
* Keep them in alphabetical order.
*/
#define AGGFNOID 0
#define AMNAME 1
#define AMOID 2
#define AMOPOPID 3
#define AMOPSTRATEGY 4
#define AMPROCNUM 5
#define ATTNAME 6
#define ATTNUM 7
#define AUTHMEMMEMROLE 8
#define AUTHMEMROLEMEM 9
#define AUTHNAME 10
#define AUTHOID 11
#define CASTSOURCETARGET 12
#define CLAAMNAMENSP 13
#define CLAOID 14
#define CONDEFAULT 15
#define CONNAMENSP 16
#define CONOID 17
#define DATABASEOID 18
#define INDEXRELID 19
#define LANGNAME 20
#define LANGOID 21
#define NAMESPACENAME 22
#define NAMESPACEOID 23
#define OPERNAMENSP 24
#define OPEROID 25
#define OPFAMILYAMNAMENSP 26
#define OPFAMILYOID 27
#define PROCNAMEARGSNSP 28
#define PROCOID 29
#define RELNAMENSP 30
#define RELOID 31
#define RULERELNAME 32
#define STATRELATT 33
#define TYPENAMENSP 34
#define TYPEOID 35
#define AGGFNOID 0
#define AMNAME 1
#define AMOID 2
#define AMOPOPID 3
#define AMOPSTRATEGY 4
#define AMPROCNUM 5
#define ATTNAME 6
#define ATTNUM 7
#define AUTHMEMMEMROLE 8
#define AUTHMEMROLEMEM 9
#define AUTHNAME 10
#define AUTHOID 11
#define CASTSOURCETARGET 12
#define CLAAMNAMENSP 13
#define CLAOID 14
#define CONDEFAULT 15
#define CONNAMENSP 16
#define CONSTROID 17
#define CONVOID 18
#define DATABASEOID 19
#define INDEXRELID 20
#define LANGNAME 21
#define LANGOID 22
#define NAMESPACENAME 23
#define NAMESPACEOID 24
#define OPERNAMENSP 25
#define OPEROID 26
#define OPFAMILYAMNAMENSP 27
#define OPFAMILYOID 28
#define PROCNAMEARGSNSP 29
#define PROCOID 30
#define RELNAMENSP 31
#define RELOID 32
#define RULERELNAME 33
#define STATRELATT 34
#define TYPENAMENSP 35
#define TYPEOID 36
extern void InitCatalogCache(void);
extern void InitCatalogCachePhase2(void);

View File

@ -195,8 +195,9 @@ DROP TABLE tmp2;
-- is run in parallel with foreign_key.sql.
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
INSERT INTO PKTABLE VALUES(42);
CREATE TEMP TABLE FKTABLE (ftest1 inet);
-- This next should fail, because inet=int does not exist
-- This next should fail, because int=inet does not exist
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
@ -205,21 +206,40 @@ DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and i
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
-- This should succeed, even though they are different types
-- because varchar=int does exist
DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 varchar);
-- This should succeed, even though they are different types,
-- because int=int8 exists and is a member of the integer opfamily
CREATE TEMP TABLE FKTABLE (ftest1 int8);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
WARNING: foreign key constraint "fktable_ftest1_fkey1" will require costly sequential scans
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
DROP TABLE pktable cascade;
NOTICE: drop cascades to constraint fktable_ftest1_fkey1 on table fktable
NOTICE: drop cascades to constraint fktable_ftest1_fkey on table fktable
DROP TABLE fktable;
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
DROP TABLE FKTABLE;
-- This should fail, because we'd have to cast numeric to int which is
-- not an implicit coercion (or use numeric=numeric, but that's not part
-- of the integer opfamily)
CREATE TEMP TABLE FKTABLE (ftest1 numeric);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: numeric and integer.
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
-- On the other hand, this should work because int implicitly promotes to
-- numeric, and we allow promotion on the FK side
CREATE TEMP TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
INSERT INTO PKTABLE VALUES(42);
CREATE TEMP TABLE FKTABLE (ftest1 int);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
PRIMARY KEY(ptest1, ptest2));
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"

View File

@ -646,7 +646,7 @@ SELECT * from FKTABLE;
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".
CONTEXT: SQL statement "UPDATE ONLY "public"."fktable" SET "ftest2" = DEFAULT WHERE "ftest1" = $1 AND "ftest2" = $2 AND "ftest3" = $3"
CONTEXT: SQL statement "UPDATE ONLY "public"."fktable" SET "ftest2" = DEFAULT WHERE $1 OPERATOR(pg_catalog.=) "ftest1" AND $2 OPERATOR(pg_catalog.=) "ftest2" AND $3 OPERATOR(pg_catalog.=) "ftest3""
-- Try to update something that will set default
UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
UPDATE PKTABLE set ptest2=10 where ptest2=4;
@ -749,7 +749,8 @@ DROP TABLE PKTABLE;
-- Basic one column, two table setup
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-- This next should fail, because inet=int does not exist
INSERT INTO PKTABLE VALUES(42);
-- This next should fail, because int=inet does not exist
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
@ -758,16 +759,41 @@ DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and i
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
-- This should succeed (with a warning), even though they are different types
-- because int=varchar does exist
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
-- This should succeed, even though they are different types,
-- because int=int8 exists and is a member of the integer opfamily
CREATE TABLE FKTABLE (ftest1 int8 REFERENCES pktable);
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
DROP TABLE FKTABLE;
-- As should this
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
-- This should fail, because we'd have to cast numeric to int which is
-- not an implicit coercion (or use numeric=numeric, but that's not part
-- of the integer opfamily)
CREATE TABLE FKTABLE (ftest1 numeric REFERENCES pktable);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: numeric and integer.
DROP TABLE PKTABLE;
-- On the other hand, this should work because int implicitly promotes to
-- numeric, and we allow promotion on the FK side
CREATE TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
INSERT INTO PKTABLE VALUES(42);
CREATE TABLE FKTABLE (ftest1 int REFERENCES pktable);
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
-- Two columns, two tables
@ -1083,21 +1109,24 @@ CREATE TEMP TABLE fktable (
x5 INT2
);
-- check individual constraints with alter table.
-- should generate warnings
-- should fail
-- varchar does not promote to real
ALTER TABLE fktable ADD CONSTRAINT fk_2_3
FOREIGN KEY (x2) REFERENCES pktable(id3);
WARNING: foreign key constraint "fk_2_3" will require costly sequential scans
DETAIL: Key columns "x2" and "id3" are of different types: character varying and real.
ERROR: foreign key constraint "fk_2_3" cannot be implemented
DETAIL: Key columns "x2" and "id3" are of incompatible types: character varying and real.
-- nor to int4
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
FOREIGN KEY (x2) REFERENCES pktable(id1);
WARNING: foreign key constraint "fk_2_1" will require costly sequential scans
DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer.
ERROR: foreign key constraint "fk_2_1" cannot be implemented
DETAIL: Key columns "x2" and "id1" are of incompatible types: character varying and integer.
-- real does not promote to int4
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
FOREIGN KEY (x3) REFERENCES pktable(id1);
WARNING: foreign key constraint "fk_3_1" will require costly sequential scans
DETAIL: Key columns "x3" and "id1" are of different types: real and integer.
-- should NOT generate warnings
-- int4 promotes to text, so this is ok
ERROR: foreign key constraint "fk_3_1" cannot be implemented
DETAIL: Key columns "x3" and "id1" are of incompatible types: real and integer.
-- should succeed
-- int4 promotes to text, so this is allowed (though pretty durn debatable)
ALTER TABLE fktable ADD CONSTRAINT fk_1_2
FOREIGN KEY (x1) REFERENCES pktable(id2);
-- int4 promotes to real
@ -1106,45 +1135,36 @@ FOREIGN KEY (x1) REFERENCES pktable(id3);
-- text is compatible with varchar
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
FOREIGN KEY (x4) REFERENCES pktable(id2);
-- int2 is part of int4 opclass as of 8.0
-- int2 is part of integer opfamily as of 8.0
ALTER TABLE fktable ADD CONSTRAINT fk_5_1
FOREIGN KEY (x5) REFERENCES pktable(id1);
-- check multikey cases, especially out-of-order column lists
-- no warnings here
-- these should work
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
ALTER TABLE fktable ADD CONSTRAINT fk_213_213
FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
-- warnings here
-- these should fail
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
WARNING: foreign key constraint "fk_123_231" will require costly sequential scans
DETAIL: Key columns "x2" and "id3" are of different types: character varying and real.
WARNING: foreign key constraint "fk_123_231" will require costly sequential scans
DETAIL: Key columns "x3" and "id1" are of different types: real and integer.
ERROR: foreign key constraint "fk_123_231" cannot be implemented
DETAIL: Key columns "x2" and "id3" are of incompatible types: character varying and real.
ALTER TABLE fktable ADD CONSTRAINT fk_241_132
FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
WARNING: foreign key constraint "fk_241_132" will require costly sequential scans
DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer.
WARNING: foreign key constraint "fk_241_132" will require costly sequential scans
DETAIL: Key columns "x4" and "id3" are of different types: text and real.
ERROR: foreign key constraint "fk_241_132" cannot be implemented
DETAIL: Key columns "x2" and "id1" are of incompatible types: character varying and integer.
DROP TABLE pktable, fktable CASCADE;
NOTICE: drop cascades to constraint fk_241_132 on table fktable
NOTICE: drop cascades to constraint fk_123_231 on table fktable
NOTICE: drop cascades to constraint fk_253_213 on table fktable
NOTICE: drop cascades to constraint fk_213_213 on table fktable
NOTICE: drop cascades to constraint fk_123_123 on table fktable
NOTICE: drop cascades to constraint fk_5_1 on table fktable
NOTICE: drop cascades to constraint fk_3_1 on table fktable
NOTICE: drop cascades to constraint fk_2_1 on table fktable
NOTICE: drop cascades to constraint fktable_x1_fkey on table fktable
NOTICE: drop cascades to constraint fk_4_2 on table fktable
NOTICE: drop cascades to constraint fk_1_2 on table fktable
NOTICE: drop cascades to constraint fktable_x2_fkey on table fktable
NOTICE: drop cascades to constraint fk_1_3 on table fktable
NOTICE: drop cascades to constraint fk_2_3 on table fktable
NOTICE: drop cascades to constraint fktable_x3_fkey on table fktable
-- test a tricky case: we can elide firing the FK check trigger during
-- an UPDATE if the UPDATE did not change the foreign key

View File

@ -241,21 +241,40 @@ DROP TABLE tmp2;
-- is run in parallel with foreign_key.sql.
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
INSERT INTO PKTABLE VALUES(42);
CREATE TEMP TABLE FKTABLE (ftest1 inet);
-- This next should fail, because inet=int does not exist
-- This next should fail, because int=inet does not exist
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- This should also fail for the same reason, but here we
-- give the column name
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-- This should succeed, even though they are different types
-- because varchar=int does exist
DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 varchar);
-- This should succeed, even though they are different types,
-- because int=int8 exists and is a member of the integer opfamily
CREATE TEMP TABLE FKTABLE (ftest1 int8);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
DROP TABLE pktable cascade;
DROP TABLE fktable;
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
DROP TABLE FKTABLE;
-- This should fail, because we'd have to cast numeric to int which is
-- not an implicit coercion (or use numeric=numeric, but that's not part
-- of the integer opfamily)
CREATE TEMP TABLE FKTABLE (ftest1 numeric);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
-- On the other hand, this should work because int implicitly promotes to
-- numeric, and we allow promotion on the FK side
CREATE TEMP TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
INSERT INTO PKTABLE VALUES(42);
CREATE TEMP TABLE FKTABLE (ftest1 int);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
PRIMARY KEY(ptest1, ptest2));

View File

@ -444,17 +444,36 @@ DROP TABLE PKTABLE;
--
-- Basic one column, two table setup
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
-- This next should fail, because inet=int does not exist
INSERT INTO PKTABLE VALUES(42);
-- This next should fail, because int=inet does not exist
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
-- This should also fail for the same reason, but here we
-- give the column name
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
-- This should succeed (with a warning), even though they are different types
-- because int=varchar does exist
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
-- This should succeed, even though they are different types,
-- because int=int8 exists and is a member of the integer opfamily
CREATE TABLE FKTABLE (ftest1 int8 REFERENCES pktable);
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
DROP TABLE FKTABLE;
-- As should this
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
-- This should fail, because we'd have to cast numeric to int which is
-- not an implicit coercion (or use numeric=numeric, but that's not part
-- of the integer opfamily)
CREATE TABLE FKTABLE (ftest1 numeric REFERENCES pktable);
DROP TABLE PKTABLE;
-- On the other hand, this should work because int implicitly promotes to
-- numeric, and we allow promotion on the FK side
CREATE TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
INSERT INTO PKTABLE VALUES(42);
CREATE TABLE FKTABLE (ftest1 int REFERENCES pktable);
-- Check it actually works
INSERT INTO FKTABLE VALUES(42); -- should succeed
INSERT INTO FKTABLE VALUES(43); -- should fail
UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
@ -727,20 +746,23 @@ CREATE TEMP TABLE fktable (
-- check individual constraints with alter table.
-- should generate warnings
-- should fail
-- varchar does not promote to real
ALTER TABLE fktable ADD CONSTRAINT fk_2_3
FOREIGN KEY (x2) REFERENCES pktable(id3);
-- nor to int4
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
FOREIGN KEY (x2) REFERENCES pktable(id1);
-- real does not promote to int4
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
FOREIGN KEY (x3) REFERENCES pktable(id1);
-- should NOT generate warnings
-- should succeed
-- int4 promotes to text, so this is ok
-- int4 promotes to text, so this is allowed (though pretty durn debatable)
ALTER TABLE fktable ADD CONSTRAINT fk_1_2
FOREIGN KEY (x1) REFERENCES pktable(id2);
@ -752,13 +774,13 @@ FOREIGN KEY (x1) REFERENCES pktable(id3);
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
FOREIGN KEY (x4) REFERENCES pktable(id2);
-- int2 is part of int4 opclass as of 8.0
-- int2 is part of integer opfamily as of 8.0
ALTER TABLE fktable ADD CONSTRAINT fk_5_1
FOREIGN KEY (x5) REFERENCES pktable(id1);
-- check multikey cases, especially out-of-order column lists
-- no warnings here
-- these should work
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
@ -769,7 +791,7 @@ FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
-- warnings here
-- these should fail
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);