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
|
Documentation of the system catalogs, directed toward PostgreSQL developers
|
||||||
-->
|
-->
|
||||||
@ -1870,6 +1870,27 @@
|
|||||||
<entry>If a foreign key, list of the referenced columns</entry>
|
<entry>If a foreign key, list of the referenced columns</entry>
|
||||||
</row>
|
</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>
|
<row>
|
||||||
<entry><structfield>conbin</structfield></entry>
|
<entry><structfield>conbin</structfield></entry>
|
||||||
<entry><type>text</type></entry>
|
<entry><type>text</type></entry>
|
||||||
@ -1899,8 +1920,8 @@
|
|||||||
<note>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
<literal>pg_class.relchecks</literal> needs to agree with the
|
<literal>pg_class.relchecks</literal> needs to agree with the
|
||||||
number of check-constraint entries found in this table for the
|
number of check-constraint entries found in this table for each
|
||||||
given relation.
|
relation.
|
||||||
</para>
|
</para>
|
||||||
</note>
|
</note>
|
||||||
|
|
||||||
@ -4166,35 +4187,42 @@
|
|||||||
<entry><structfield>tgisconstraint</structfield></entry>
|
<entry><structfield>tgisconstraint</structfield></entry>
|
||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
<entry></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>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>tgconstrname</structfield></entry>
|
<entry><structfield>tgconstrname</structfield></entry>
|
||||||
<entry><type>name</type></entry>
|
<entry><type>name</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>Referential integrity constraint name</entry>
|
<entry>Constraint name, if a constraint trigger</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>tgconstrrelid</structfield></entry>
|
<entry><structfield>tgconstrrelid</structfield></entry>
|
||||||
<entry><type>oid</type></entry>
|
<entry><type>oid</type></entry>
|
||||||
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></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>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>tgdeferrable</structfield></entry>
|
<entry><structfield>tgdeferrable</structfield></entry>
|
||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>True if deferrable</entry>
|
<entry>True if constraint trigger is deferrable</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>tginitdeferred</structfield></entry>
|
<entry><structfield>tginitdeferred</structfield></entry>
|
||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>True if initially deferred</entry>
|
<entry>True if constraint trigger is initially deferred</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
@ -4221,10 +4249,22 @@
|
|||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</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>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
<literal>pg_class.reltriggers</literal> needs to agree with the
|
<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>
|
</para>
|
||||||
</note>
|
</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">
|
<chapter id="triggers">
|
||||||
<title>Triggers</title>
|
<title>Triggers</title>
|
||||||
@ -467,6 +467,7 @@ typedef struct Trigger
|
|||||||
bool tgenabled;
|
bool tgenabled;
|
||||||
bool tgisconstraint;
|
bool tgisconstraint;
|
||||||
Oid tgconstrrelid;
|
Oid tgconstrrelid;
|
||||||
|
Oid tgconstraint;
|
||||||
bool tgdeferrable;
|
bool tgdeferrable;
|
||||||
bool tginitdeferred;
|
bool tginitdeferred;
|
||||||
int16 tgnargs;
|
int16 tgnargs;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* See acl.h.
|
* See acl.h.
|
||||||
@ -2314,7 +2314,7 @@ pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
|
|||||||
if (superuser_arg(roleid))
|
if (superuser_arg(roleid))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
tuple = SearchSysCache(CONOID,
|
tuple = SearchSysCache(CONVOID,
|
||||||
ObjectIdGetDatum(conv_oid),
|
ObjectIdGetDatum(conv_oid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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:
|
case OCLASS_CONSTRAINT:
|
||||||
{
|
{
|
||||||
Relation conDesc;
|
HeapTuple conTup;
|
||||||
ScanKeyData skey[1];
|
|
||||||
SysScanDesc rcscan;
|
|
||||||
HeapTuple tup;
|
|
||||||
Form_pg_constraint con;
|
Form_pg_constraint con;
|
||||||
|
|
||||||
conDesc = heap_open(ConstraintRelationId, AccessShareLock);
|
conTup = SearchSysCache(CONSTROID,
|
||||||
|
ObjectIdGetDatum(object->objectId),
|
||||||
ScanKeyInit(&skey[0],
|
0, 0, 0);
|
||||||
ObjectIdAttributeNumber,
|
if (!HeapTupleIsValid(conTup))
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
elog(ERROR, "cache lookup failed for constraint %u",
|
||||||
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",
|
|
||||||
object->objectId);
|
object->objectId);
|
||||||
|
con = (Form_pg_constraint) GETSTRUCT(conTup);
|
||||||
con = (Form_pg_constraint) GETSTRUCT(tup);
|
|
||||||
|
|
||||||
if (OidIsValid(con->conrelid))
|
if (OidIsValid(con->conrelid))
|
||||||
{
|
{
|
||||||
@ -1794,8 +1781,7 @@ getObjectDescription(const ObjectAddress *object)
|
|||||||
NameStr(con->conname));
|
NameStr(con->conname));
|
||||||
}
|
}
|
||||||
|
|
||||||
systable_endscan(rcscan);
|
ReleaseSysCache(conTup);
|
||||||
heap_close(conDesc, AccessShareLock);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1803,7 +1789,7 @@ getObjectDescription(const ObjectAddress *object)
|
|||||||
{
|
{
|
||||||
HeapTuple conTup;
|
HeapTuple conTup;
|
||||||
|
|
||||||
conTup = SearchSysCache(CONOID,
|
conTup = SearchSysCache(CONVOID,
|
||||||
ObjectIdGetDatum(object->objectId),
|
ObjectIdGetDatum(object->objectId),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(conTup))
|
if (!HeapTupleIsValid(conTup))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
@ -1463,6 +1463,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
|
|||||||
InvalidOid, /* not a domain constraint */
|
InvalidOid, /* not a domain constraint */
|
||||||
InvalidOid, /* Foreign key fields */
|
InvalidOid, /* Foreign key fields */
|
||||||
NULL,
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
@ -680,6 +680,9 @@ index_create(Oid heapRelationId,
|
|||||||
InvalidOid, /* no domain */
|
InvalidOid, /* no domain */
|
||||||
InvalidOid, /* no foreign key */
|
InvalidOid, /* no foreign key */
|
||||||
NULL,
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
Oid connamespace;
|
||||||
bool visible;
|
bool visible;
|
||||||
|
|
||||||
contup = SearchSysCache(CONOID,
|
contup = SearchSysCache(CONVOID,
|
||||||
ObjectIdGetDatum(conid),
|
ObjectIdGetDatum(conid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(contup))
|
if (!HeapTupleIsValid(contup))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/pg_constraint.h"
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_depend.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_trigger.h"
|
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
@ -49,6 +48,9 @@ CreateConstraintEntry(const char *constraintName,
|
|||||||
Oid domainId,
|
Oid domainId,
|
||||||
Oid foreignRelId,
|
Oid foreignRelId,
|
||||||
const int16 *foreignKey,
|
const int16 *foreignKey,
|
||||||
|
const Oid *pfEqOp,
|
||||||
|
const Oid *ppEqOp,
|
||||||
|
const Oid *ffEqOp,
|
||||||
int foreignNKeys,
|
int foreignNKeys,
|
||||||
char foreignUpdateType,
|
char foreignUpdateType,
|
||||||
char foreignDeleteType,
|
char foreignDeleteType,
|
||||||
@ -65,6 +67,9 @@ CreateConstraintEntry(const char *constraintName,
|
|||||||
Datum values[Natts_pg_constraint];
|
Datum values[Natts_pg_constraint];
|
||||||
ArrayType *conkeyArray;
|
ArrayType *conkeyArray;
|
||||||
ArrayType *confkeyArray;
|
ArrayType *confkeyArray;
|
||||||
|
ArrayType *conpfeqopArray;
|
||||||
|
ArrayType *conppeqopArray;
|
||||||
|
ArrayType *conffeqopArray;
|
||||||
NameData cname;
|
NameData cname;
|
||||||
int i;
|
int i;
|
||||||
ObjectAddress conobject;
|
ObjectAddress conobject;
|
||||||
@ -92,16 +97,33 @@ CreateConstraintEntry(const char *constraintName,
|
|||||||
|
|
||||||
if (foreignNKeys > 0)
|
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++)
|
for (i = 0; i < foreignNKeys; i++)
|
||||||
confkey[i] = Int16GetDatum(foreignKey[i]);
|
fkdatums[i] = Int16GetDatum(foreignKey[i]);
|
||||||
confkeyArray = construct_array(confkey, foreignNKeys,
|
confkeyArray = construct_array(fkdatums, foreignNKeys,
|
||||||
INT2OID, 2, true, 's');
|
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
|
else
|
||||||
|
{
|
||||||
confkeyArray = NULL;
|
confkeyArray = NULL;
|
||||||
|
conpfeqopArray = NULL;
|
||||||
|
conppeqopArray = NULL;
|
||||||
|
conffeqopArray = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* initialize nulls and values */
|
/* initialize nulls and values */
|
||||||
for (i = 0; i < Natts_pg_constraint; i++)
|
for (i = 0; i < Natts_pg_constraint; i++)
|
||||||
@ -132,6 +154,21 @@ CreateConstraintEntry(const char *constraintName,
|
|||||||
else
|
else
|
||||||
nulls[Anum_pg_constraint_confkey - 1] = 'n';
|
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.
|
* initialize the binary form of the check constraint.
|
||||||
*/
|
*/
|
||||||
@ -246,6 +283,36 @@ CreateConstraintEntry(const char *constraintName,
|
|||||||
recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
|
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)
|
if (conExpr != NULL)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -419,24 +486,16 @@ void
|
|||||||
RemoveConstraintById(Oid conId)
|
RemoveConstraintById(Oid conId)
|
||||||
{
|
{
|
||||||
Relation conDesc;
|
Relation conDesc;
|
||||||
ScanKeyData skey[1];
|
|
||||||
SysScanDesc conscan;
|
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_constraint con;
|
Form_pg_constraint con;
|
||||||
|
|
||||||
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
|
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
ScanKeyInit(&skey[0],
|
tup = SearchSysCache(CONSTROID,
|
||||||
ObjectIdAttributeNumber,
|
ObjectIdGetDatum(conId),
|
||||||
BTEqualStrategyNumber, F_OIDEQ,
|
0, 0, 0);
|
||||||
ObjectIdGetDatum(conId));
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||||
|
elog(ERROR, "cache lookup failed for constraint %u", 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);
|
|
||||||
con = (Form_pg_constraint) GETSTRUCT(tup);
|
con = (Form_pg_constraint) GETSTRUCT(tup);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -505,97 +564,10 @@ RemoveConstraintById(Oid conId)
|
|||||||
simple_heap_delete(conDesc, &tup->t_self);
|
simple_heap_delete(conDesc, &tup->t_self);
|
||||||
|
|
||||||
/* Clean up */
|
/* Clean up */
|
||||||
systable_endscan(conscan);
|
ReleaseSysCache(tup);
|
||||||
heap_close(conDesc, RowExclusiveLock);
|
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
|
* AlterConstraintNamespaces
|
||||||
* Find any constraints belonging to the specified object,
|
* Find any constraints belonging to the specified object,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
HeapTuple tuple;
|
||||||
ObjectAddress object;
|
ObjectAddress object;
|
||||||
|
|
||||||
tuple = SearchSysCache(CONOID,
|
tuple = SearchSysCache(CONVOID,
|
||||||
ObjectIdGetDatum(conversionOid),
|
ObjectIdGetDatum(conversionOid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
@ -313,7 +313,7 @@ pg_convert_using(PG_FUNCTION_ARGS)
|
|||||||
errmsg("conversion \"%s\" does not exist",
|
errmsg("conversion \"%s\" does not exist",
|
||||||
NameListToString(parsed_name))));
|
NameListToString(parsed_name))));
|
||||||
|
|
||||||
tuple = SearchSysCache(CONOID,
|
tuple = SearchSysCache(CONVOID,
|
||||||
ObjectIdGetDatum(convoid),
|
ObjectIdGetDatum(convoid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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",
|
errmsg("conversion \"%s\" does not exist",
|
||||||
NameListToString(name))));
|
NameListToString(name))));
|
||||||
|
|
||||||
tup = SearchSysCacheCopy(CONOID,
|
tup = SearchSysCacheCopy(CONVOID,
|
||||||
ObjectIdGetDatum(conversionOid),
|
ObjectIdGetDatum(conversionOid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||||
@ -236,7 +236,7 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
|
|||||||
|
|
||||||
Assert(RelationGetRelid(rel) == ConversionRelationId);
|
Assert(RelationGetRelid(rel) == ConversionRelationId);
|
||||||
|
|
||||||
tup = SearchSysCacheCopy(CONOID,
|
tup = SearchSysCacheCopy(CONVOID,
|
||||||
ObjectIdGetDatum(conversionOid),
|
ObjectIdGetDatum(conversionOid),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
if (instr->ntuples == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (trig->tgisconstraint &&
|
if (OidIsValid(trig->tgconstraint) &&
|
||||||
(conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)
|
(conname = get_constraint_name(trig->tgconstraint)) != NULL)
|
||||||
{
|
{
|
||||||
appendStringInfo(&buf, "Trigger for constraint %s",
|
appendStringInfo(&buf, "Trigger for constraint %s",
|
||||||
conname);
|
conname);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_depend.h"
|
||||||
#include "catalog/pg_inherits.h"
|
#include "catalog/pg_inherits.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "catalog/toasting.h"
|
#include "catalog/toasting.h"
|
||||||
@ -139,6 +140,7 @@ typedef struct NewConstraint
|
|||||||
char *name; /* Constraint name, or NULL if none */
|
char *name; /* Constraint name, or NULL if none */
|
||||||
ConstrType contype; /* CHECK or FOREIGN */
|
ConstrType contype; /* CHECK or FOREIGN */
|
||||||
Oid refrelid; /* PK rel, if FOREIGN */
|
Oid refrelid; /* PK rel, if FOREIGN */
|
||||||
|
Oid conid; /* OID of pg_constraint entry, if FOREIGN */
|
||||||
Node *qual; /* Check expr or FkConstraint struct */
|
Node *qual; /* Check expr or FkConstraint struct */
|
||||||
List *qualstate; /* Execution state for CHECK */
|
List *qualstate; /* Execution state for CHECK */
|
||||||
} NewConstraint;
|
} NewConstraint;
|
||||||
@ -186,10 +188,9 @@ static Oid transformFkeyCheckAttrs(Relation pkrel,
|
|||||||
int numattrs, int16 *attnums,
|
int numattrs, int16 *attnums,
|
||||||
Oid *opclasses);
|
Oid *opclasses);
|
||||||
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
||||||
Relation rel, Relation pkrel);
|
Relation rel, Relation pkrel, Oid constraintOid);
|
||||||
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||||
Oid constrOid);
|
Oid constraintOid);
|
||||||
static char *fkMatchTypeToString(char match_type);
|
|
||||||
static void ATController(Relation rel, List *cmds, bool recurse);
|
static void ATController(Relation rel, List *cmds, bool recurse);
|
||||||
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
||||||
bool recurse, bool recursing);
|
bool recurse, bool recursing);
|
||||||
@ -256,11 +257,6 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
|
|||||||
static void ATExecAddInherit(Relation rel, RangeVar *parent);
|
static void ATExecAddInherit(Relation rel, RangeVar *parent);
|
||||||
static void ATExecDropInherit(Relation rel, RangeVar *parent);
|
static void ATExecDropInherit(Relation rel, RangeVar *parent);
|
||||||
static void copy_relation_data(Relation rel, SMgrRelation dst);
|
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);
|
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 */
|
relation_close(targetrelation, NoLock); /* close rel but keep lock */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1708,226 +1689,12 @@ renamerel(Oid myrelid, const char *newrelname)
|
|||||||
if (relkind != RELKIND_INDEX)
|
if (relkind != RELKIND_INDEX)
|
||||||
TypeRename(oldrelname, namespaceId, newrelname);
|
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!
|
* Close rel, but keep exclusive lock!
|
||||||
*/
|
*/
|
||||||
relation_close(targetrelation, NoLock);
|
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
|
* AlterTable
|
||||||
* Execute ALTER TABLE, which can be a list of subcommands
|
* Execute ALTER TABLE, which can be a list of subcommands
|
||||||
@ -2552,7 +2319,8 @@ ATRewriteTables(List **wqueue)
|
|||||||
|
|
||||||
refrel = heap_open(con->refrelid, RowShareLock);
|
refrel = heap_open(con->refrelid, RowShareLock);
|
||||||
|
|
||||||
validateForeignKeyConstraint(fkconstraint, rel, refrel);
|
validateForeignKeyConstraint(fkconstraint, rel, refrel,
|
||||||
|
con->conid);
|
||||||
|
|
||||||
heap_close(refrel, NoLock);
|
heap_close(refrel, NoLock);
|
||||||
}
|
}
|
||||||
@ -4061,6 +3829,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
Oid pktypoid[INDEX_MAX_KEYS];
|
Oid pktypoid[INDEX_MAX_KEYS];
|
||||||
Oid fktypoid[INDEX_MAX_KEYS];
|
Oid fktypoid[INDEX_MAX_KEYS];
|
||||||
Oid opclasses[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 i;
|
||||||
int numfks,
|
int numfks,
|
||||||
numpks;
|
numpks;
|
||||||
@ -4138,6 +3909,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
MemSet(pktypoid, 0, sizeof(pktypoid));
|
MemSet(pktypoid, 0, sizeof(pktypoid));
|
||||||
MemSet(fktypoid, 0, sizeof(fktypoid));
|
MemSet(fktypoid, 0, sizeof(fktypoid));
|
||||||
MemSet(opclasses, 0, sizeof(opclasses));
|
MemSet(opclasses, 0, sizeof(opclasses));
|
||||||
|
MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
|
||||||
|
MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
|
||||||
|
MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
|
||||||
|
|
||||||
numfks = transformColumnNameList(RelationGetRelid(rel),
|
numfks = transformColumnNameList(RelationGetRelid(rel),
|
||||||
fkconstraint->fk_attrs,
|
fkconstraint->fk_attrs,
|
||||||
@ -4166,7 +3940,15 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
opclasses);
|
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)
|
if (numfks != numpks)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
|
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
|
||||||
@ -4174,24 +3956,71 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
|
|
||||||
for (i = 0; i < numpks; i++)
|
for (i = 0; i < numpks; i++)
|
||||||
{
|
{
|
||||||
/*
|
HeapTuple cla_ht;
|
||||||
* pktypoid[i] is the primary key table's i'th key's type fktypoid[i]
|
Form_pg_opclass cla_tup;
|
||||||
* is the foreign key table's i'th key's type
|
Oid amid;
|
||||||
*
|
Oid opfamily;
|
||||||
* Note that we look for an operator with the PK type on the left;
|
Oid pktype;
|
||||||
* when the types are different this is critical because the PK index
|
Oid fktype;
|
||||||
* will need operators with the indexkey on the left. (Ordinarily both
|
Oid pfeqop;
|
||||||
* commutator operators will exist if either does, but we won't get
|
Oid ppeqop;
|
||||||
* the right answer from the test below on opclass membership unless
|
Oid ffeqop;
|
||||||
* we select the proper operator.)
|
int16 eqstrategy;
|
||||||
*/
|
|
||||||
Operator o = oper(NULL, list_make1(makeString("=")),
|
|
||||||
pktypoid[i], fktypoid[i],
|
|
||||||
true, -1);
|
|
||||||
|
|
||||||
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,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("foreign key constraint \"%s\" "
|
errmsg("foreign key constraint \"%s\" "
|
||||||
"cannot be implemented",
|
"cannot be implemented",
|
||||||
fkconstraint->constr_name),
|
fkconstraint->constr_name),
|
||||||
@ -4202,41 +4031,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
format_type_be(fktypoid[i]),
|
format_type_be(fktypoid[i]),
|
||||||
format_type_be(pktypoid[i]))));
|
format_type_be(pktypoid[i]))));
|
||||||
|
|
||||||
/*
|
pfeqoperators[i] = pfeqop;
|
||||||
* Check that the found operator is compatible with the PK index, and
|
ppeqoperators[i] = ppeqop;
|
||||||
* generate a warning if not, since otherwise costly seqscans will be
|
ffeqoperators[i] = ffeqop;
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4254,6 +4051,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
* constraint */
|
* constraint */
|
||||||
RelationGetRelid(pkrel),
|
RelationGetRelid(pkrel),
|
||||||
pkattnum,
|
pkattnum,
|
||||||
|
pfeqoperators,
|
||||||
|
ppeqoperators,
|
||||||
|
ffeqoperators,
|
||||||
numpks,
|
numpks,
|
||||||
fkconstraint->fk_upd_action,
|
fkconstraint->fk_upd_action,
|
||||||
fkconstraint->fk_del_action,
|
fkconstraint->fk_del_action,
|
||||||
@ -4268,6 +4068,24 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
*/
|
*/
|
||||||
createForeignKeyTriggers(rel, fkconstraint, constrOid);
|
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.
|
* Close pk table, but keep lock until we've committed.
|
||||||
*/
|
*/
|
||||||
@ -4526,25 +4344,15 @@ transformFkeyCheckAttrs(Relation pkrel,
|
|||||||
static void
|
static void
|
||||||
validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
||||||
Relation rel,
|
Relation rel,
|
||||||
Relation pkrel)
|
Relation pkrel,
|
||||||
|
Oid constraintOid)
|
||||||
{
|
{
|
||||||
HeapScanDesc scan;
|
HeapScanDesc scan;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Trigger trig;
|
Trigger trig;
|
||||||
ListCell *list;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See if we can do it with a single LEFT JOIN query. A FALSE result
|
* Build a trigger call structure; we'll need it either way.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
MemSet(&trig, 0, sizeof(trig));
|
MemSet(&trig, 0, sizeof(trig));
|
||||||
trig.tgoid = InvalidOid;
|
trig.tgoid = InvalidOid;
|
||||||
@ -4552,35 +4360,23 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
|||||||
trig.tgenabled = TRUE;
|
trig.tgenabled = TRUE;
|
||||||
trig.tgisconstraint = TRUE;
|
trig.tgisconstraint = TRUE;
|
||||||
trig.tgconstrrelid = RelationGetRelid(pkrel);
|
trig.tgconstrrelid = RelationGetRelid(pkrel);
|
||||||
|
trig.tgconstraint = constraintOid;
|
||||||
trig.tgdeferrable = FALSE;
|
trig.tgdeferrable = FALSE;
|
||||||
trig.tginitdeferred = FALSE;
|
trig.tginitdeferred = FALSE;
|
||||||
|
/* we needn't fill in tgargs */
|
||||||
|
|
||||||
trig.tgargs = (char **) palloc(sizeof(char *) *
|
/*
|
||||||
(4 + list_length(fkconstraint->fk_attrs)
|
* See if we can do it with a single LEFT JOIN query. A FALSE result
|
||||||
+ list_length(fkconstraint->pk_attrs)));
|
* indicates we must proceed with the fire-the-trigger method.
|
||||||
|
*/
|
||||||
trig.tgargs[0] = trig.tgname;
|
if (RI_Initial_Check(&trig, rel, pkrel))
|
||||||
trig.tgargs[1] = RelationGetRelationName(rel);
|
return;
|
||||||
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;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
|
||||||
|
|
||||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||||
@ -4613,18 +4409,13 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
|||||||
}
|
}
|
||||||
|
|
||||||
heap_endscan(scan);
|
heap_endscan(scan);
|
||||||
|
|
||||||
pfree(trig.tgargs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
|
CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
|
||||||
ObjectAddress *constrobj, ObjectAddress *trigobj,
|
Oid constraintOid, bool on_insert)
|
||||||
bool on_insert)
|
|
||||||
{
|
{
|
||||||
CreateTrigStmt *fk_trigger;
|
CreateTrigStmt *fk_trigger;
|
||||||
ListCell *fk_attr;
|
|
||||||
ListCell *pk_attr;
|
|
||||||
|
|
||||||
fk_trigger = makeNode(CreateTrigStmt);
|
fk_trigger = makeNode(CreateTrigStmt);
|
||||||
fk_trigger->trigname = fkconstraint->constr_name;
|
fk_trigger->trigname = fkconstraint->constr_name;
|
||||||
@ -4649,32 +4440,9 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
|
|||||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||||
fk_trigger->constrrel = fkconstraint->pktable;
|
fk_trigger->constrrel = fkconstraint->pktable;
|
||||||
|
|
||||||
fk_trigger->args = NIL;
|
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,
|
(void) CreateTrigger(fk_trigger, constraintOid);
|
||||||
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);
|
|
||||||
|
|
||||||
/* Make changes-so-far visible */
|
/* Make changes-so-far visible */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -4685,14 +4453,10 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||||
Oid constrOid)
|
Oid constraintOid)
|
||||||
{
|
{
|
||||||
RangeVar *myRel;
|
RangeVar *myRel;
|
||||||
CreateTrigStmt *fk_trigger;
|
CreateTrigStmt *fk_trigger;
|
||||||
ListCell *fk_attr;
|
|
||||||
ListCell *pk_attr;
|
|
||||||
ObjectAddress trigobj,
|
|
||||||
constrobj;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reconstruct a RangeVar for my relation (not passed in, unfortunately).
|
* 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)),
|
myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
|
||||||
pstrdup(RelationGetRelationName(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 */
|
/* Make changes-so-far visible */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
|
|
||||||
@ -4716,8 +4471,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
|||||||
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK
|
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK
|
||||||
* action for both INSERTs and UPDATEs on the referencing table.
|
* action for both INSERTs and UPDATEs on the referencing table.
|
||||||
*/
|
*/
|
||||||
CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, true);
|
CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, true);
|
||||||
CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, false);
|
CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, false);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
|
* 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);
|
(int) fkconstraint->fk_del_action);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fk_trigger->args = NIL;
|
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);
|
(void) CreateTrigger(fk_trigger, constraintOid);
|
||||||
|
|
||||||
/* Register dependency from trigger to constraint */
|
|
||||||
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
|
|
||||||
|
|
||||||
/* Make changes-so-far visible */
|
/* Make changes-so-far visible */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -4835,49 +4572,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
|||||||
(int) fkconstraint->fk_upd_action);
|
(int) fkconstraint->fk_upd_action);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fk_trigger->args = NIL;
|
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);
|
(void) CreateTrigger(fk_trigger, constraintOid);
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/catalog.h"
|
||||||
#include "catalog/dependency.h"
|
#include "catalog/dependency.h"
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_type.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.
|
* Create a trigger. Returns the OID of the created trigger.
|
||||||
*
|
*
|
||||||
* forConstraint, if true, says that this trigger is being created to
|
* constraintOid, if nonzero, says that this trigger is being created to
|
||||||
* implement a constraint. The caller will then be expected to make
|
* implement that constraint. A suitable pg_depend entry will be made
|
||||||
* a pg_depend entry linking the trigger to that constraint (and thereby
|
* to link the trigger to that constraint.
|
||||||
* to the owning relation(s)).
|
|
||||||
*/
|
*/
|
||||||
Oid
|
Oid
|
||||||
CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
|
||||||
{
|
{
|
||||||
int16 tgtype;
|
int16 tgtype;
|
||||||
int2vector *tgattr;
|
int2vector *tgattr;
|
||||||
@ -91,51 +91,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
|
|
||||||
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
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)
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
@ -152,15 +107,17 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
|
|
||||||
if (stmt->isconstraint)
|
if (stmt->isconstraint)
|
||||||
{
|
{
|
||||||
/* foreign key constraint trigger */
|
/* constraint trigger */
|
||||||
|
|
||||||
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
|
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
|
||||||
ACL_REFERENCES);
|
ACL_REFERENCES);
|
||||||
if (aclresult != ACLCHECK_OK)
|
if (aclresult != ACLCHECK_OK)
|
||||||
aclcheck_error(aclresult, ACL_KIND_CLASS,
|
aclcheck_error(aclresult, ACL_KIND_CLASS,
|
||||||
RelationGetRelationName(rel));
|
RelationGetRelationName(rel));
|
||||||
if (constrrelid != InvalidOid)
|
|
||||||
|
if (stmt->constrrel != NULL)
|
||||||
{
|
{
|
||||||
|
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
|
||||||
|
|
||||||
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
|
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
|
||||||
ACL_REFERENCES);
|
ACL_REFERENCES);
|
||||||
if (aclresult != ACLCHECK_OK)
|
if (aclresult != ACLCHECK_OK)
|
||||||
@ -170,7 +127,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* real trigger */
|
/* regular trigger */
|
||||||
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
|
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
|
||||||
ACL_TRIGGER);
|
ACL_TRIGGER);
|
||||||
if (aclresult != ACLCHECK_OK)
|
if (aclresult != ACLCHECK_OK)
|
||||||
@ -187,23 +144,31 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
trigoid = GetNewOid(tgrel);
|
trigoid = GetNewOid(tgrel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If trigger is an RI constraint, use specified trigger name as
|
* If trigger is for an RI constraint, the passed-in name is the
|
||||||
* constraint name and build a unique trigger name instead. This is mainly
|
* constraint name; save that and build a unique trigger name to avoid
|
||||||
* for backwards compatibility with CREATE CONSTRAINT TRIGGER commands.
|
* collisions with user-selected trigger names.
|
||||||
*/
|
*/
|
||||||
if (stmt->isconstraint)
|
if (OidIsValid(constraintOid))
|
||||||
{
|
{
|
||||||
snprintf(constrtrigname, sizeof(constrtrigname),
|
snprintf(constrtrigname, sizeof(constrtrigname),
|
||||||
"RI_ConstraintTrigger_%u", trigoid);
|
"RI_ConstraintTrigger_%u", trigoid);
|
||||||
trigname = constrtrigname;
|
trigname = constrtrigname;
|
||||||
constrname = stmt->trigname;
|
constrname = stmt->trigname;
|
||||||
}
|
}
|
||||||
|
else if (stmt->isconstraint)
|
||||||
|
{
|
||||||
|
/* constraint trigger: trigger name is also constraint name */
|
||||||
|
trigname = stmt->trigname;
|
||||||
|
constrname = stmt->trigname;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* regular trigger: use empty constraint name */
|
||||||
trigname = stmt->trigname;
|
trigname = stmt->trigname;
|
||||||
constrname = "";
|
constrname = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Compute tgtype */
|
||||||
TRIGGER_CLEAR_TYPE(tgtype);
|
TRIGGER_CLEAR_TYPE(tgtype);
|
||||||
if (stmt->before)
|
if (stmt->before)
|
||||||
TRIGGER_SETT_BEFORE(tgtype);
|
TRIGGER_SETT_BEFORE(tgtype);
|
||||||
@ -298,7 +263,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
/*
|
/*
|
||||||
* Build the new pg_trigger tuple.
|
* 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_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
|
||||||
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
|
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,
|
values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
|
||||||
CStringGetDatum(constrname));
|
CStringGetDatum(constrname));
|
||||||
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
|
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_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
|
||||||
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
|
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
|
||||||
|
|
||||||
@ -372,10 +338,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
|
|
||||||
CatalogUpdateIndexes(tgrel, tuple);
|
CatalogUpdateIndexes(tgrel, tuple);
|
||||||
|
|
||||||
myself.classId = TriggerRelationId;
|
|
||||||
myself.objectId = trigoid;
|
|
||||||
myself.objectSubId = 0;
|
|
||||||
|
|
||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
heap_close(tgrel, RowExclusiveLock);
|
heap_close(tgrel, RowExclusiveLock);
|
||||||
|
|
||||||
@ -412,20 +374,36 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Record dependencies for trigger. Always place a normal dependency on
|
* Record dependencies for trigger. Always place a normal dependency on
|
||||||
* the function. If we are doing this in response to an explicit CREATE
|
* the function.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
myself.classId = TriggerRelationId;
|
||||||
|
myself.objectId = trigoid;
|
||||||
|
myself.objectSubId = 0;
|
||||||
|
|
||||||
referenced.classId = ProcedureRelationId;
|
referenced.classId = ProcedureRelationId;
|
||||||
referenced.objectId = funcoid;
|
referenced.objectId = funcoid;
|
||||||
referenced.objectSubId = 0;
|
referenced.objectSubId = 0;
|
||||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
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.classId = RelationRelationId;
|
||||||
referenced.objectId = RelationGetRelid(rel);
|
referenced.objectId = RelationGetRelid(rel);
|
||||||
referenced.objectSubId = 0;
|
referenced.objectSubId = 0;
|
||||||
@ -773,7 +751,7 @@ EnableDisableTrigger(Relation rel, const char *tgname,
|
|||||||
{
|
{
|
||||||
Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
|
Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
|
||||||
|
|
||||||
if (oldtrig->tgisconstraint)
|
if (OidIsValid(oldtrig->tgconstraint))
|
||||||
{
|
{
|
||||||
/* system trigger ... ok to process? */
|
/* system trigger ... ok to process? */
|
||||||
if (skip_system)
|
if (skip_system)
|
||||||
@ -886,6 +864,7 @@ RelationBuildTriggers(Relation relation)
|
|||||||
build->tgenabled = pg_trigger->tgenabled;
|
build->tgenabled = pg_trigger->tgenabled;
|
||||||
build->tgisconstraint = pg_trigger->tgisconstraint;
|
build->tgisconstraint = pg_trigger->tgisconstraint;
|
||||||
build->tgconstrrelid = pg_trigger->tgconstrrelid;
|
build->tgconstrrelid = pg_trigger->tgconstrrelid;
|
||||||
|
build->tgconstraint = pg_trigger->tgconstraint;
|
||||||
build->tgdeferrable = pg_trigger->tgdeferrable;
|
build->tgdeferrable = pg_trigger->tgdeferrable;
|
||||||
build->tginitdeferred = pg_trigger->tginitdeferred;
|
build->tginitdeferred = pg_trigger->tginitdeferred;
|
||||||
build->tgnargs = pg_trigger->tgnargs;
|
build->tgnargs = pg_trigger->tgnargs;
|
||||||
@ -1220,6 +1199,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
|
|||||||
return false;
|
return false;
|
||||||
if (trig1->tgconstrrelid != trig2->tgconstrrelid)
|
if (trig1->tgconstrrelid != trig2->tgconstrrelid)
|
||||||
return false;
|
return false;
|
||||||
|
if (trig1->tgconstraint != trig2->tgconstraint)
|
||||||
|
return false;
|
||||||
if (trig1->tgdeferrable != trig2->tgdeferrable)
|
if (trig1->tgdeferrable != trig2->tgdeferrable)
|
||||||
return false;
|
return false;
|
||||||
if (trig1->tginitdeferred != trig2->tginitdeferred)
|
if (trig1->tginitdeferred != trig2->tginitdeferred)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* 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 */
|
domainOid, /* domain constraint */
|
||||||
InvalidOid, /* Foreign key fields */
|
InvalidOid, /* Foreign key fields */
|
||||||
NULL,
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
0,
|
0,
|
||||||
' ',
|
' ',
|
||||||
' ',
|
' ',
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
break;
|
||||||
|
|
||||||
case T_CreateTrigStmt:
|
case T_CreateTrigStmt:
|
||||||
CreateTrigger((CreateTrigStmt *) parsetree, false);
|
CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_DropPropertyStmt:
|
case T_DropPropertyStmt:
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
||||||
int prettyFlags)
|
int prettyFlags)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
|
||||||
Relation conDesc;
|
|
||||||
SysScanDesc conscan;
|
|
||||||
ScanKeyData skey[1];
|
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
Form_pg_constraint conForm;
|
Form_pg_constraint conForm;
|
||||||
|
StringInfoData buf;
|
||||||
|
|
||||||
/*
|
tup = SearchSysCache(CONSTROID,
|
||||||
* Fetch the pg_constraint row. There's no syscache for pg_constraint so
|
ObjectIdGetDatum(constraintId),
|
||||||
* we must do it the hard way.
|
0, 0, 0);
|
||||||
*/
|
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||||
conDesc = heap_open(ConstraintRelationId, AccessShareLock);
|
elog(ERROR, "cache lookup failed for constraint %u", constraintId);
|
||||||
|
|
||||||
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);
|
|
||||||
conForm = (Form_pg_constraint) GETSTRUCT(tup);
|
conForm = (Form_pg_constraint) GETSTRUCT(tup);
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
@ -922,8 +907,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
appendStringInfo(&buf, "FOREIGN KEY (");
|
appendStringInfo(&buf, "FOREIGN KEY (");
|
||||||
|
|
||||||
/* Fetch and build referencing-column list */
|
/* Fetch and build referencing-column list */
|
||||||
val = heap_getattr(tup, Anum_pg_constraint_conkey,
|
val = SysCacheGetAttr(CONSTROID, tup,
|
||||||
RelationGetDescr(conDesc), &isnull);
|
Anum_pg_constraint_conkey, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "null conkey for constraint %u",
|
elog(ERROR, "null conkey for constraint %u",
|
||||||
constraintId);
|
constraintId);
|
||||||
@ -935,8 +920,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
generate_relation_name(conForm->confrelid));
|
generate_relation_name(conForm->confrelid));
|
||||||
|
|
||||||
/* Fetch and build referenced-column list */
|
/* Fetch and build referenced-column list */
|
||||||
val = heap_getattr(tup, Anum_pg_constraint_confkey,
|
val = SysCacheGetAttr(CONSTROID, tup,
|
||||||
RelationGetDescr(conDesc), &isnull);
|
Anum_pg_constraint_confkey, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "null confkey for constraint %u",
|
elog(ERROR, "null confkey for constraint %u",
|
||||||
constraintId);
|
constraintId);
|
||||||
@ -1038,8 +1023,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
appendStringInfo(&buf, "UNIQUE (");
|
appendStringInfo(&buf, "UNIQUE (");
|
||||||
|
|
||||||
/* Fetch and build target column list */
|
/* Fetch and build target column list */
|
||||||
val = heap_getattr(tup, Anum_pg_constraint_conkey,
|
val = SysCacheGetAttr(CONSTROID, tup,
|
||||||
RelationGetDescr(conDesc), &isnull);
|
Anum_pg_constraint_conkey, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "null conkey for constraint %u",
|
elog(ERROR, "null conkey for constraint %u",
|
||||||
constraintId);
|
constraintId);
|
||||||
@ -1071,8 +1056,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
List *context;
|
List *context;
|
||||||
|
|
||||||
/* Fetch constraint expression in parsetree form */
|
/* Fetch constraint expression in parsetree form */
|
||||||
val = heap_getattr(tup, Anum_pg_constraint_conbin,
|
val = SysCacheGetAttr(CONSTROID, tup,
|
||||||
RelationGetDescr(conDesc), &isnull);
|
Anum_pg_constraint_conbin, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "null conbin for constraint %u",
|
elog(ERROR, "null conbin for constraint %u",
|
||||||
constraintId);
|
constraintId);
|
||||||
@ -1115,8 +1100,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
systable_endscan(conscan);
|
ReleaseSysCache(tup);
|
||||||
heap_close(conDesc, AccessShareLock);
|
|
||||||
|
|
||||||
return buf.data;
|
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
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Eventually, the index information should go through here, too.
|
* Eventually, the index information should go through here, too.
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#include "bootstrap/bootstrap.h"
|
#include "bootstrap/bootstrap.h"
|
||||||
#include "catalog/pg_amop.h"
|
#include "catalog/pg_amop.h"
|
||||||
#include "catalog/pg_amproc.h"
|
#include "catalog/pg_amproc.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
@ -897,10 +898,37 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
|
|||||||
ReleaseSysCache(tp);
|
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 ---------- */
|
/* ---------- OPCLASS CACHE ---------- */
|
||||||
|
|
||||||
|
17
src/backend/utils/cache/syscache.c
vendored
17
src/backend/utils/cache/syscache.c
vendored
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* These routines allow the parser/planner/executor to perform
|
* These routines allow the parser/planner/executor to perform
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#include "catalog/pg_auth_members.h"
|
#include "catalog/pg_auth_members.h"
|
||||||
#include "catalog/pg_authid.h"
|
#include "catalog/pg_authid.h"
|
||||||
#include "catalog/pg_cast.h"
|
#include "catalog/pg_cast.h"
|
||||||
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_conversion.h"
|
#include "catalog/pg_conversion.h"
|
||||||
#include "catalog/pg_database.h"
|
#include "catalog/pg_database.h"
|
||||||
#include "catalog/pg_language.h"
|
#include "catalog/pg_language.h"
|
||||||
@ -298,7 +299,19 @@ static const struct cachedesc cacheinfo[] = {
|
|||||||
},
|
},
|
||||||
128
|
128
|
||||||
},
|
},
|
||||||
{ConversionRelationId, /* CONOID */
|
{ConstraintRelationId, /* CONSTROID */
|
||||||
|
ConstraintOidIndexId,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
ObjectIdAttributeNumber,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
},
|
||||||
|
1024
|
||||||
|
},
|
||||||
|
{ConversionRelationId, /* CONVOID */
|
||||||
ConversionOidIndexId,
|
ConversionOidIndexId,
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* by PostgreSQL
|
* by PostgreSQL
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
|
||||||
|
|
||||||
resetPQExpBuffer(query);
|
resetPQExpBuffer(query);
|
||||||
if (g_fout->remoteVersion >= 70300)
|
if (g_fout->remoteVersion >= 80300)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We ignore triggers that are tied to a foreign-key constraint
|
* 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,
|
appendPQExpBuffer(query,
|
||||||
"SELECT tgname, "
|
"SELECT tgname, "
|
||||||
"tgfoid::pg_catalog.regproc as tgfname, "
|
"tgfoid::pg_catalog.regproc as tgfname, "
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "describe.h"
|
#include "describe.h"
|
||||||
@ -1128,12 +1128,8 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
"FROM pg_catalog.pg_trigger t\n"
|
"FROM pg_catalog.pg_trigger t\n"
|
||||||
"WHERE t.tgrelid = '%s' "
|
"WHERE t.tgrelid = '%s' "
|
||||||
"AND t.tgenabled "
|
"AND t.tgenabled "
|
||||||
"AND (NOT t.tgisconstraint "
|
"AND t.tgconstraint = 0\n"
|
||||||
" OR NOT EXISTS"
|
"ORDER BY 1",
|
||||||
" (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",
|
|
||||||
oid);
|
oid);
|
||||||
result4 = PSQLexec(buf.data, false);
|
result4 = PSQLexec(buf.data, false);
|
||||||
if (!result4)
|
if (!result4)
|
||||||
@ -1152,12 +1148,8 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
"FROM pg_catalog.pg_trigger t\n"
|
"FROM pg_catalog.pg_trigger t\n"
|
||||||
"WHERE t.tgrelid = '%s' "
|
"WHERE t.tgrelid = '%s' "
|
||||||
"AND NOT t.tgenabled "
|
"AND NOT t.tgenabled "
|
||||||
"AND (NOT t.tgisconstraint "
|
"AND t.tgconstraint = 0\n"
|
||||||
" OR NOT EXISTS"
|
"ORDER BY 1",
|
||||||
" (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",
|
|
||||||
oid);
|
oid);
|
||||||
result7 = PSQLexec(buf.data, false);
|
result7 = PSQLexec(buf.data, false);
|
||||||
if (!result7)
|
if (!result7)
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200702081
|
#define CATALOG_VERSION_NO 200702131
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* 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));
|
DECLARE_INDEX(pg_trigger_tgconstrname_index, 2699, on pg_trigger using btree(tgconstrname name_ops));
|
||||||
#define TriggerConstrNameIndexId 2699
|
#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));
|
DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using btree(tgrelid oid_ops, tgname name_ops));
|
||||||
#define TriggerRelidNameIndexId 2701
|
#define TriggerRelidNameIndexId 2701
|
||||||
DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));
|
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) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
@ -91,6 +91,24 @@ CATALOG(pg_constraint,2606)
|
|||||||
*/
|
*/
|
||||||
int2 confkey[1];
|
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
|
* If a check constraint, nodeToString representation of expression
|
||||||
*/
|
*/
|
||||||
@ -113,7 +131,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
|
|||||||
* compiler constants for 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_conname 1
|
||||||
#define Anum_pg_constraint_connamespace 2
|
#define Anum_pg_constraint_connamespace 2
|
||||||
#define Anum_pg_constraint_contype 3
|
#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_confmatchtype 11
|
||||||
#define Anum_pg_constraint_conkey 12
|
#define Anum_pg_constraint_conkey 12
|
||||||
#define Anum_pg_constraint_confkey 13
|
#define Anum_pg_constraint_confkey 13
|
||||||
#define Anum_pg_constraint_conbin 14
|
#define Anum_pg_constraint_conpfeqop 14
|
||||||
#define Anum_pg_constraint_consrc 15
|
#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 */
|
/* Valid values for contype */
|
||||||
@ -167,6 +188,9 @@ extern Oid CreateConstraintEntry(const char *constraintName,
|
|||||||
Oid domainId,
|
Oid domainId,
|
||||||
Oid foreignRelId,
|
Oid foreignRelId,
|
||||||
const int16 *foreignKey,
|
const int16 *foreignKey,
|
||||||
|
const Oid *pfEqOp,
|
||||||
|
const Oid *ppEqOp,
|
||||||
|
const Oid *ffEqOp,
|
||||||
int foreignNKeys,
|
int foreignNKeys,
|
||||||
char foreignUpdateType,
|
char foreignUpdateType,
|
||||||
char foreignDeleteType,
|
char foreignDeleteType,
|
||||||
@ -184,8 +208,6 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
|
|||||||
const char *label, Oid namespace,
|
const char *label, Oid namespace,
|
||||||
List *others);
|
List *others);
|
||||||
|
|
||||||
extern char *GetConstraintNameForTrigger(Oid triggerId);
|
|
||||||
|
|
||||||
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
|
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
|
||||||
Oid newNspId, bool isType);
|
Oid newNspId, bool isType);
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* pg_trigger.h
|
* 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) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
@ -26,23 +29,30 @@
|
|||||||
/* ----------------
|
/* ----------------
|
||||||
* pg_trigger definition. cpp turns this into
|
* pg_trigger definition. cpp turns this into
|
||||||
* typedef struct FormData_pg_trigger
|
* 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
|
#define TriggerRelationId 2620
|
||||||
|
|
||||||
CATALOG(pg_trigger,2620)
|
CATALOG(pg_trigger,2620)
|
||||||
{
|
{
|
||||||
Oid tgrelid; /* triggered relation */
|
Oid tgrelid; /* relation trigger is attached to */
|
||||||
NameData tgname; /* trigger' name */
|
NameData tgname; /* trigger's name */
|
||||||
Oid tgfoid; /* OID of function to be called */
|
Oid tgfoid; /* OID of function to be called */
|
||||||
int2 tgtype; /* BEFORE/AFTER UPDATE/DELETE/INSERT
|
int2 tgtype; /* BEFORE/AFTER UPDATE/DELETE/INSERT
|
||||||
* ROW/STATEMENT */
|
* ROW/STATEMENT; see below */
|
||||||
bool tgenabled; /* trigger is enabled/disabled */
|
bool tgenabled; /* trigger is enabled/disabled */
|
||||||
bool tgisconstraint; /* trigger is a RI constraint */
|
bool tgisconstraint; /* trigger is a constraint trigger */
|
||||||
NameData tgconstrname; /* RI constraint name */
|
NameData tgconstrname; /* constraint name */
|
||||||
Oid tgconstrrelid; /* RI table of foreign key definition */
|
Oid tgconstrrelid; /* constraint's FROM table, if any */
|
||||||
bool tgdeferrable; /* RI trigger is deferrable */
|
Oid tgconstraint; /* owning pg_constraint entry, if any */
|
||||||
bool tginitdeferred; /* RI trigger is deferred initially */
|
bool tgdeferrable; /* constraint trigger is deferrable */
|
||||||
|
bool tginitdeferred; /* constraint trigger is deferred initially */
|
||||||
int2 tgnargs; /* # of extra arguments in tgargs */
|
int2 tgnargs; /* # of extra arguments in tgargs */
|
||||||
|
|
||||||
/* VARIABLE LENGTH FIELDS: */
|
/* VARIABLE LENGTH FIELDS: */
|
||||||
@ -61,7 +71,7 @@ typedef FormData_pg_trigger *Form_pg_trigger;
|
|||||||
* compiler constants for 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_tgrelid 1
|
||||||
#define Anum_pg_trigger_tgname 2
|
#define Anum_pg_trigger_tgname 2
|
||||||
#define Anum_pg_trigger_tgfoid 3
|
#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_tgisconstraint 6
|
||||||
#define Anum_pg_trigger_tgconstrname 7
|
#define Anum_pg_trigger_tgconstrname 7
|
||||||
#define Anum_pg_trigger_tgconstrrelid 8
|
#define Anum_pg_trigger_tgconstrrelid 8
|
||||||
#define Anum_pg_trigger_tgdeferrable 9
|
#define Anum_pg_trigger_tgconstraint 9
|
||||||
#define Anum_pg_trigger_tginitdeferred 10
|
#define Anum_pg_trigger_tgdeferrable 10
|
||||||
#define Anum_pg_trigger_tgnargs 11
|
#define Anum_pg_trigger_tginitdeferred 11
|
||||||
#define Anum_pg_trigger_tgattr 12
|
#define Anum_pg_trigger_tgnargs 12
|
||||||
#define Anum_pg_trigger_tgargs 13
|
#define Anum_pg_trigger_tgattr 13
|
||||||
|
#define Anum_pg_trigger_tgargs 14
|
||||||
|
|
||||||
|
/* Bits within tgtype */
|
||||||
#define TRIGGER_TYPE_ROW (1 << 0)
|
#define TRIGGER_TYPE_ROW (1 << 0)
|
||||||
#define TRIGGER_TYPE_BEFORE (1 << 1)
|
#define TRIGGER_TYPE_BEFORE (1 << 1)
|
||||||
#define TRIGGER_TYPE_INSERT (1 << 2)
|
#define TRIGGER_TYPE_INSERT (1 << 2)
|
||||||
#define TRIGGER_TYPE_DELETE (1 << 3)
|
#define TRIGGER_TYPE_DELETE (1 << 3)
|
||||||
#define TRIGGER_TYPE_UPDATE (1 << 4)
|
#define TRIGGER_TYPE_UPDATE (1 << 4)
|
||||||
|
|
||||||
|
/* Macros for manipulating tgtype */
|
||||||
#define TRIGGER_CLEAR_TYPE(type) ((type) = 0)
|
#define TRIGGER_CLEAR_TYPE(type) ((type) = 0)
|
||||||
|
|
||||||
#define TRIGGER_SETT_ROW(type) ((type) |= TRIGGER_TYPE_ROW)
|
#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) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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) \
|
#define TRIGGER_FIRED_AFTER(event) \
|
||||||
(!TRIGGER_FIRED_BEFORE (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
|
extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid);
|
||||||
#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 void DropTrigger(Oid relid, const char *trigname,
|
extern void DropTrigger(Oid relid, const char *trigname,
|
||||||
DropBehavior behavior, bool missing_ok);
|
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);
|
HeapTuple old_row, HeapTuple new_row);
|
||||||
extern bool RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
|
extern bool RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
|
||||||
HeapTuple old_row, HeapTuple new_row);
|
HeapTuple old_row, HeapTuple new_row);
|
||||||
extern bool RI_Initial_Check(FkConstraint *fkconstraint,
|
extern bool RI_Initial_Check(Trigger *trigger,
|
||||||
Relation rel,
|
Relation fk_rel, Relation pk_rel);
|
||||||
Relation pkrel);
|
|
||||||
|
|
||||||
|
/* result values for RI_FKey_trigger_type: */
|
||||||
#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */
|
#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_FK 2 /* is a trigger on the FK relation */
|
||||||
#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */
|
#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) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 int32 get_atttypmod(Oid relid, AttrNumber attnum);
|
||||||
extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
|
extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
|
||||||
Oid *typid, int32 *typmod);
|
Oid *typid, int32 *typmod);
|
||||||
|
extern char *get_constraint_name(Oid conoid);
|
||||||
extern Oid get_opclass_family(Oid opclass);
|
extern Oid get_opclass_family(Oid opclass);
|
||||||
extern Oid get_opclass_input_type(Oid opclass);
|
extern Oid get_opclass_input_type(Oid opclass);
|
||||||
extern RegProcedure get_opcode(Oid opno);
|
extern RegProcedure get_opcode(Oid opno);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 tgenabled;
|
||||||
bool tgisconstraint;
|
bool tgisconstraint;
|
||||||
Oid tgconstrrelid;
|
Oid tgconstrrelid;
|
||||||
|
Oid tgconstraint;
|
||||||
bool tgdeferrable;
|
bool tgdeferrable;
|
||||||
bool tginitdeferred;
|
bool tginitdeferred;
|
||||||
int16 tgnargs;
|
int16 tgnargs;
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.67 2007/01/05 22:19:59 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.68 2007/02/14 01:58:58 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -45,25 +45,26 @@
|
|||||||
#define CLAOID 14
|
#define CLAOID 14
|
||||||
#define CONDEFAULT 15
|
#define CONDEFAULT 15
|
||||||
#define CONNAMENSP 16
|
#define CONNAMENSP 16
|
||||||
#define CONOID 17
|
#define CONSTROID 17
|
||||||
#define DATABASEOID 18
|
#define CONVOID 18
|
||||||
#define INDEXRELID 19
|
#define DATABASEOID 19
|
||||||
#define LANGNAME 20
|
#define INDEXRELID 20
|
||||||
#define LANGOID 21
|
#define LANGNAME 21
|
||||||
#define NAMESPACENAME 22
|
#define LANGOID 22
|
||||||
#define NAMESPACEOID 23
|
#define NAMESPACENAME 23
|
||||||
#define OPERNAMENSP 24
|
#define NAMESPACEOID 24
|
||||||
#define OPEROID 25
|
#define OPERNAMENSP 25
|
||||||
#define OPFAMILYAMNAMENSP 26
|
#define OPEROID 26
|
||||||
#define OPFAMILYOID 27
|
#define OPFAMILYAMNAMENSP 27
|
||||||
#define PROCNAMEARGSNSP 28
|
#define OPFAMILYOID 28
|
||||||
#define PROCOID 29
|
#define PROCNAMEARGSNSP 29
|
||||||
#define RELNAMENSP 30
|
#define PROCOID 30
|
||||||
#define RELOID 31
|
#define RELNAMENSP 31
|
||||||
#define RULERELNAME 32
|
#define RELOID 32
|
||||||
#define STATRELATT 33
|
#define RULERELNAME 33
|
||||||
#define TYPENAMENSP 34
|
#define STATRELATT 34
|
||||||
#define TYPEOID 35
|
#define TYPENAMENSP 35
|
||||||
|
#define TYPEOID 36
|
||||||
|
|
||||||
extern void InitCatalogCache(void);
|
extern void InitCatalogCache(void);
|
||||||
extern void InitCatalogCachePhase2(void);
|
extern void InitCatalogCachePhase2(void);
|
||||||
|
@ -195,8 +195,9 @@ DROP TABLE tmp2;
|
|||||||
-- is run in parallel with foreign_key.sql.
|
-- is run in parallel with foreign_key.sql.
|
||||||
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
||||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
|
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);
|
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;
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
|
||||||
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
|
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);
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
||||||
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
|
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;
|
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;
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
|
||||||
WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
|
-- Check it actually works
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
|
INSERT INTO FKTABLE VALUES(42); -- should succeed
|
||||||
-- As should this
|
INSERT INTO FKTABLE VALUES(43); -- should fail
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
|
||||||
WARNING: foreign key constraint "fktable_ftest1_fkey1" will require costly sequential scans
|
DETAIL: Key (ftest1)=(43) is not present in table "pktable".
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
|
DROP TABLE FKTABLE;
|
||||||
DROP TABLE pktable cascade;
|
-- This should fail, because we'd have to cast numeric to int which is
|
||||||
NOTICE: drop cascades to constraint fktable_ftest1_fkey1 on table fktable
|
-- not an implicit coercion (or use numeric=numeric, but that's not part
|
||||||
NOTICE: drop cascades to constraint fktable_ftest1_fkey on table fktable
|
-- of the integer opfamily)
|
||||||
DROP TABLE fktable;
|
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,
|
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
|
||||||
PRIMARY KEY(ptest1, ptest2));
|
PRIMARY KEY(ptest1, ptest2));
|
||||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
|
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;
|
UPDATE PKTABLE set ptest2=5 where ptest2=2;
|
||||||
ERROR: insert or update on table "fktable" violates foreign key constraint "constrname3"
|
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".
|
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
|
-- Try to update something that will set default
|
||||||
UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
|
UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
|
||||||
UPDATE PKTABLE set ptest2=10 where ptest2=4;
|
UPDATE PKTABLE set ptest2=10 where ptest2=4;
|
||||||
@ -749,7 +749,8 @@ DROP TABLE PKTABLE;
|
|||||||
-- Basic one column, two table setup
|
-- Basic one column, two table setup
|
||||||
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
||||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
|
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);
|
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
|
||||||
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
|
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));
|
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
|
||||||
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
|
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
|
-- This should succeed, even though they are different types,
|
||||||
-- because int=varchar does exist
|
-- because int=int8 exists and is a member of the integer opfamily
|
||||||
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
|
CREATE TABLE FKTABLE (ftest1 int8 REFERENCES pktable);
|
||||||
WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
|
-- Check it actually works
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
|
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 FKTABLE;
|
||||||
-- As should this
|
-- This should fail, because we'd have to cast numeric to int which is
|
||||||
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
|
-- not an implicit coercion (or use numeric=numeric, but that's not part
|
||||||
WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
|
-- of the integer opfamily)
|
||||||
DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
|
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 FKTABLE;
|
||||||
DROP TABLE PKTABLE;
|
DROP TABLE PKTABLE;
|
||||||
-- Two columns, two tables
|
-- Two columns, two tables
|
||||||
@ -1083,21 +1109,24 @@ CREATE TEMP TABLE fktable (
|
|||||||
x5 INT2
|
x5 INT2
|
||||||
);
|
);
|
||||||
-- check individual constraints with alter table.
|
-- 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
|
ALTER TABLE fktable ADD CONSTRAINT fk_2_3
|
||||||
FOREIGN KEY (x2) REFERENCES pktable(id3);
|
FOREIGN KEY (x2) REFERENCES pktable(id3);
|
||||||
WARNING: foreign key constraint "fk_2_3" will require costly sequential scans
|
ERROR: foreign key constraint "fk_2_3" cannot be implemented
|
||||||
DETAIL: Key columns "x2" and "id3" are of different types: character varying and real.
|
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
|
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
|
||||||
FOREIGN KEY (x2) REFERENCES pktable(id1);
|
FOREIGN KEY (x2) REFERENCES pktable(id1);
|
||||||
WARNING: foreign key constraint "fk_2_1" will require costly sequential scans
|
ERROR: foreign key constraint "fk_2_1" cannot be implemented
|
||||||
DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer.
|
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
|
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
|
||||||
FOREIGN KEY (x3) REFERENCES pktable(id1);
|
FOREIGN KEY (x3) REFERENCES pktable(id1);
|
||||||
WARNING: foreign key constraint "fk_3_1" will require costly sequential scans
|
ERROR: foreign key constraint "fk_3_1" cannot be implemented
|
||||||
DETAIL: Key columns "x3" and "id1" are of different types: real and integer.
|
DETAIL: Key columns "x3" and "id1" are of incompatible types: real and integer.
|
||||||
-- 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
|
ALTER TABLE fktable ADD CONSTRAINT fk_1_2
|
||||||
FOREIGN KEY (x1) REFERENCES pktable(id2);
|
FOREIGN KEY (x1) REFERENCES pktable(id2);
|
||||||
-- int4 promotes to real
|
-- int4 promotes to real
|
||||||
@ -1106,45 +1135,36 @@ FOREIGN KEY (x1) REFERENCES pktable(id3);
|
|||||||
-- text is compatible with varchar
|
-- text is compatible with varchar
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
|
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
|
||||||
FOREIGN KEY (x4) REFERENCES pktable(id2);
|
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
|
ALTER TABLE fktable ADD CONSTRAINT fk_5_1
|
||||||
FOREIGN KEY (x5) REFERENCES pktable(id1);
|
FOREIGN KEY (x5) REFERENCES pktable(id1);
|
||||||
-- check multikey cases, especially out-of-order column lists
|
-- check multikey cases, especially out-of-order column lists
|
||||||
-- no warnings here
|
-- these should work
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
|
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
|
||||||
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
|
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_213_213
|
ALTER TABLE fktable ADD CONSTRAINT fk_213_213
|
||||||
FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
|
FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
|
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
|
||||||
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
|
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
|
||||||
-- warnings here
|
-- these should fail
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
|
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
|
||||||
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
|
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
|
||||||
WARNING: foreign key constraint "fk_123_231" will require costly sequential scans
|
ERROR: foreign key constraint "fk_123_231" cannot be implemented
|
||||||
DETAIL: Key columns "x2" and "id3" are of different types: character varying and real.
|
DETAIL: Key columns "x2" and "id3" are of incompatible 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.
|
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_241_132
|
ALTER TABLE fktable ADD CONSTRAINT fk_241_132
|
||||||
FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
|
FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
|
||||||
WARNING: foreign key constraint "fk_241_132" will require costly sequential scans
|
ERROR: foreign key constraint "fk_241_132" cannot be implemented
|
||||||
DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer.
|
DETAIL: Key columns "x2" and "id1" are of incompatible 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.
|
|
||||||
DROP TABLE pktable, fktable CASCADE;
|
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_253_213 on table fktable
|
||||||
NOTICE: drop cascades to constraint fk_213_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_123_123 on table fktable
|
||||||
NOTICE: drop cascades to constraint fk_5_1 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 fktable_x1_fkey on table fktable
|
||||||
NOTICE: drop cascades to constraint fk_4_2 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 fk_1_2 on table fktable
|
||||||
NOTICE: drop cascades to constraint fktable_x2_fkey 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_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
|
NOTICE: drop cascades to constraint fktable_x3_fkey on table fktable
|
||||||
-- test a tricky case: we can elide firing the FK check trigger during
|
-- test a tricky case: we can elide firing the FK check trigger during
|
||||||
-- an UPDATE if the UPDATE did not change the foreign key
|
-- 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.
|
-- is run in parallel with foreign_key.sql.
|
||||||
|
|
||||||
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
||||||
|
INSERT INTO PKTABLE VALUES(42);
|
||||||
CREATE TEMP TABLE FKTABLE (ftest1 inet);
|
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;
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
|
||||||
-- This should also fail for the same reason, but here we
|
-- This should also fail for the same reason, but here we
|
||||||
-- give the column name
|
-- give the column name
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
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;
|
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;
|
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
|
||||||
-- As should this
|
-- Check it actually works
|
||||||
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
|
INSERT INTO FKTABLE VALUES(42); -- should succeed
|
||||||
DROP TABLE pktable cascade;
|
INSERT INTO FKTABLE VALUES(43); -- should fail
|
||||||
DROP TABLE fktable;
|
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,
|
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
|
||||||
PRIMARY KEY(ptest1, ptest2));
|
PRIMARY KEY(ptest1, ptest2));
|
||||||
|
@ -444,17 +444,36 @@ DROP TABLE PKTABLE;
|
|||||||
--
|
--
|
||||||
-- Basic one column, two table setup
|
-- Basic one column, two table setup
|
||||||
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
|
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);
|
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
|
||||||
-- This should also fail for the same reason, but here we
|
-- This should also fail for the same reason, but here we
|
||||||
-- give the column name
|
-- give the column name
|
||||||
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
|
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
|
||||||
-- This should succeed (with a warning), even though they are different types
|
-- This should succeed, even though they are different types,
|
||||||
-- because int=varchar does exist
|
-- because int=int8 exists and is a member of the integer opfamily
|
||||||
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
|
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;
|
DROP TABLE FKTABLE;
|
||||||
-- As should this
|
-- This should fail, because we'd have to cast numeric to int which is
|
||||||
CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
|
-- 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 FKTABLE;
|
||||||
DROP TABLE PKTABLE;
|
DROP TABLE PKTABLE;
|
||||||
|
|
||||||
@ -727,20 +746,23 @@ CREATE TEMP TABLE fktable (
|
|||||||
|
|
||||||
-- check individual constraints with alter table.
|
-- 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
|
ALTER TABLE fktable ADD CONSTRAINT fk_2_3
|
||||||
FOREIGN KEY (x2) REFERENCES pktable(id3);
|
FOREIGN KEY (x2) REFERENCES pktable(id3);
|
||||||
|
|
||||||
|
-- nor to int4
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
|
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
|
||||||
FOREIGN KEY (x2) REFERENCES pktable(id1);
|
FOREIGN KEY (x2) REFERENCES pktable(id1);
|
||||||
|
|
||||||
|
-- real does not promote to int4
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
|
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
|
||||||
FOREIGN KEY (x3) REFERENCES pktable(id1);
|
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
|
ALTER TABLE fktable ADD CONSTRAINT fk_1_2
|
||||||
FOREIGN KEY (x1) REFERENCES pktable(id2);
|
FOREIGN KEY (x1) REFERENCES pktable(id2);
|
||||||
|
|
||||||
@ -752,13 +774,13 @@ FOREIGN KEY (x1) REFERENCES pktable(id3);
|
|||||||
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
|
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
|
||||||
FOREIGN KEY (x4) REFERENCES pktable(id2);
|
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
|
ALTER TABLE fktable ADD CONSTRAINT fk_5_1
|
||||||
FOREIGN KEY (x5) REFERENCES pktable(id1);
|
FOREIGN KEY (x5) REFERENCES pktable(id1);
|
||||||
|
|
||||||
-- check multikey cases, especially out-of-order column lists
|
-- check multikey cases, especially out-of-order column lists
|
||||||
|
|
||||||
-- no warnings here
|
-- these should work
|
||||||
|
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
|
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
|
||||||
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
|
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
|
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
|
||||||
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
|
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
|
||||||
|
|
||||||
-- warnings here
|
-- these should fail
|
||||||
|
|
||||||
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
|
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
|
||||||
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
|
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user