Commit 7bddca34 authored by Tom Lane's avatar Tom Lane

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.
parent 65e2f550
<!-- $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>
......
<!-- $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;
......
......@@ -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))
......
......@@ -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))
......
......@@ -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,
' ',
' ',
......
......@@ -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,
' ',
' ',
......
......@@ -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))
......
......@@ -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,
......
......@@ -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))
......
......@@ -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 */
......
......@@ -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);
......
This diff is collapsed.
......@@ -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)
......
......@@ -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,
' ',
' ',
......
......@@ -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:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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;
}
......
......@@ -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 ---------- */
......
......@@ -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,
......
......@@ -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, "
......
......@@ -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)
......
......@@ -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
......@@ -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));
......
......@@ -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);
......
/*-------------------------------------------------------------------------
*
* 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)
......
......@@ -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 */
......
......@@ -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);
......
......@@ -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;
......
......@@ -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 $
*
*-------------------------------------------------------------------------
*/
......@@ -45,25 +45,26 @@
#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 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);
......
......@@ -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"
......
......@@ -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
......
......@@ -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));
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment