mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
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:
parent
65e2f55031
commit
7bddca3450
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
34
src/backend/utils/cache/lsyscache.c
vendored
34
src/backend/utils/cache/lsyscache.c
vendored
@ -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 ---------- */
|
||||
|
||||
|
17
src/backend/utils/cache/syscache.c
vendored
17
src/backend/utils/cache/syscache.c
vendored
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user