diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index f5e15f1985..9a9f9b55be 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-
+
@@ -1870,6 +1870,27 @@
If a foreign key, list of the referenced columns
+
+ conpfeqop
+ oid[]
+ pg_operator.oid>
+ If a foreign key, list of the equality operators for PK = FK comparisons
+
+
+
+ conppeqop
+ oid[]
+ pg_operator.oid>
+ If a foreign key, list of the equality operators for PK = PK comparisons
+
+
+
+ conffeqop
+ oid[]
+ pg_operator.oid>
+ If a foreign key, list of the equality operators for FK = FK comparisons
+
+
conbin
text
@@ -1899,8 +1920,8 @@
pg_class.relchecks needs to agree with the
- number of check-constraint entries found in this table for the
- given relation.
+ number of check-constraint entries found in this table for each
+ relation.
@@ -4166,35 +4187,42 @@
tgisconstraint
bool
- True if trigger implements a referential integrity constraint
+ True if trigger is a constraint trigger>
tgconstrname
name
- Referential integrity constraint name
+ Constraint name, if a constraint trigger
tgconstrrelid
oid
pg_class.oid
- The table referenced by an referential integrity constraint
+ The table referenced by a referential integrity constraint
+
+
+
+ tgconstraint
+ oid
+ pg_constraint.oid
+ The pg_constraint> entry owning the trigger, if any
tgdeferrable
bool
- True if deferrable
+ True if constraint trigger is deferrable
tginitdeferred
bool
- True if initially deferred
+ True if constraint trigger is initially deferred
@@ -4221,10 +4249,22 @@
+
+
+ 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.
+
+
+
pg_class.reltriggers 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.
diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml
index 0479f57b0d..193b817397 100644
--- a/doc/src/sgml/trigger.sgml
+++ b/doc/src/sgml/trigger.sgml
@@ -1,4 +1,4 @@
-
+
Triggers
@@ -467,6 +467,7 @@ typedef struct Trigger
bool tgenabled;
bool tgisconstraint;
Oid tgconstrrelid;
+ Oid tgconstraint;
bool tgdeferrable;
bool tginitdeferred;
int16 tgnargs;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index b1210841ff..a2b64c78b3 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.136 2007/02/01 19:10:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.137 2007/02/14 01:58:56 tgl Exp $
*
* NOTES
* See acl.h.
@@ -2314,7 +2314,7 @@ pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
if (superuser_arg(roleid))
return true;
- tuple = SearchSysCache(CONOID,
+ tuple = SearchSysCache(CONVOID,
ObjectIdGetDatum(conv_oid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 2041125d4d..40591fd368 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.63 2007/02/01 19:10:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.64 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1758,29 +1758,16 @@ getObjectDescription(const ObjectAddress *object)
case OCLASS_CONSTRAINT:
{
- Relation conDesc;
- ScanKeyData skey[1];
- SysScanDesc rcscan;
- HeapTuple tup;
+ HeapTuple conTup;
Form_pg_constraint con;
- conDesc = heap_open(ConstraintRelationId, AccessShareLock);
-
- ScanKeyInit(&skey[0],
- ObjectIdAttributeNumber,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(object->objectId));
-
- rcscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
- SnapshotNow, 1, skey);
-
- tup = systable_getnext(rcscan);
-
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "could not find tuple for constraint %u",
+ conTup = SearchSysCache(CONSTROID,
+ ObjectIdGetDatum(object->objectId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(conTup))
+ elog(ERROR, "cache lookup failed for constraint %u",
object->objectId);
-
- con = (Form_pg_constraint) GETSTRUCT(tup);
+ con = (Form_pg_constraint) GETSTRUCT(conTup);
if (OidIsValid(con->conrelid))
{
@@ -1794,8 +1781,7 @@ getObjectDescription(const ObjectAddress *object)
NameStr(con->conname));
}
- systable_endscan(rcscan);
- heap_close(conDesc, AccessShareLock);
+ ReleaseSysCache(conTup);
break;
}
@@ -1803,7 +1789,7 @@ getObjectDescription(const ObjectAddress *object)
{
HeapTuple conTup;
- conTup = SearchSysCache(CONOID,
+ conTup = SearchSysCache(CONVOID,
ObjectIdGetDatum(object->objectId),
0, 0, 0);
if (!HeapTupleIsValid(conTup))
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 1d0639bcf0..d29a6df21d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.316 2007/01/05 22:19:24 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.317 2007/02/14 01:58:56 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -1463,6 +1463,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
InvalidOid, /* not a domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
+ NULL,
+ NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 52554c3b21..ae8965e730 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.278 2007/02/01 19:10:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.279 2007/02/14 01:58:56 tgl Exp $
*
*
* INTERFACE ROUTINES
@@ -680,6 +680,9 @@ index_create(Oid heapRelationId,
InvalidOid, /* no domain */
InvalidOid, /* no foreign key */
NULL,
+ NULL,
+ NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 7c2cd4b8a4..2213192f78 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.91 2007/02/01 19:10:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.92 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1199,7 +1199,7 @@ ConversionIsVisible(Oid conid)
Oid connamespace;
bool visible;
- contup = SearchSysCache(CONOID,
+ contup = SearchSysCache(CONVOID,
ObjectIdGetDatum(conid),
0, 0, 0);
if (!HeapTupleIsValid(contup))
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 53249facb7..ede6607b85 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.34 2007/01/05 22:19:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.35 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,8 +19,7 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
-#include "catalog/pg_depend.h"
-#include "catalog/pg_trigger.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "utils/array.h"
@@ -49,6 +48,9 @@ CreateConstraintEntry(const char *constraintName,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
+ const Oid *pfEqOp,
+ const Oid *ppEqOp,
+ const Oid *ffEqOp,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
@@ -65,6 +67,9 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *conpfeqopArray;
+ ArrayType *conppeqopArray;
+ ArrayType *conffeqopArray;
NameData cname;
int i;
ObjectAddress conobject;
@@ -92,16 +97,33 @@ CreateConstraintEntry(const char *constraintName,
if (foreignNKeys > 0)
{
- Datum *confkey;
+ Datum *fkdatums;
- confkey = (Datum *) palloc(foreignNKeys * sizeof(Datum));
+ fkdatums = (Datum *) palloc(foreignNKeys * sizeof(Datum));
for (i = 0; i < foreignNKeys; i++)
- confkey[i] = Int16GetDatum(foreignKey[i]);
- confkeyArray = construct_array(confkey, foreignNKeys,
+ fkdatums[i] = Int16GetDatum(foreignKey[i]);
+ confkeyArray = construct_array(fkdatums, foreignNKeys,
INT2OID, 2, true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
+ conpfeqopArray = construct_array(fkdatums, foreignNKeys,
+ OIDOID, sizeof(Oid), true, 'i');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = ObjectIdGetDatum(ppEqOp[i]);
+ conppeqopArray = construct_array(fkdatums, foreignNKeys,
+ OIDOID, sizeof(Oid), true, 'i');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
+ conffeqopArray = construct_array(fkdatums, foreignNKeys,
+ OIDOID, sizeof(Oid), true, 'i');
}
else
+ {
confkeyArray = NULL;
+ conpfeqopArray = NULL;
+ conppeqopArray = NULL;
+ conffeqopArray = NULL;
+ }
/* initialize nulls and values */
for (i = 0; i < Natts_pg_constraint; i++)
@@ -132,6 +154,21 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = 'n';
+ if (conpfeqopArray)
+ values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
+ else
+ nulls[Anum_pg_constraint_conpfeqop - 1] = 'n';
+
+ if (conppeqopArray)
+ values[Anum_pg_constraint_conppeqop - 1] = PointerGetDatum(conppeqopArray);
+ else
+ nulls[Anum_pg_constraint_conppeqop - 1] = 'n';
+
+ if (conffeqopArray)
+ values[Anum_pg_constraint_conffeqop - 1] = PointerGetDatum(conffeqopArray);
+ else
+ nulls[Anum_pg_constraint_conffeqop - 1] = 'n';
+
/*
* initialize the binary form of the check constraint.
*/
@@ -246,6 +283,36 @@ CreateConstraintEntry(const char *constraintName,
recordDependencyOn(&conobject, &relobject, DEPENDENCY_NORMAL);
}
+ if (foreignNKeys > 0)
+ {
+ /*
+ * Register normal dependencies on the equality operators that
+ * support a foreign-key constraint. If the PK and FK types
+ * are the same then all three operators for a column are the
+ * same; otherwise they are different.
+ */
+ ObjectAddress oprobject;
+
+ oprobject.classId = OperatorRelationId;
+ oprobject.objectSubId = 0;
+
+ for (i = 0; i < foreignNKeys; i++)
+ {
+ oprobject.objectId = pfEqOp[i];
+ recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
+ if (ppEqOp[i] != pfEqOp[i])
+ {
+ oprobject.objectId = ppEqOp[i];
+ recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
+ }
+ if (ffEqOp[i] != pfEqOp[i])
+ {
+ oprobject.objectId = ffEqOp[i];
+ recordDependencyOn(&conobject, &oprobject, DEPENDENCY_NORMAL);
+ }
+ }
+ }
+
if (conExpr != NULL)
{
/*
@@ -419,24 +486,16 @@ void
RemoveConstraintById(Oid conId)
{
Relation conDesc;
- ScanKeyData skey[1];
- SysScanDesc conscan;
HeapTuple tup;
Form_pg_constraint con;
conDesc = heap_open(ConstraintRelationId, RowExclusiveLock);
- ScanKeyInit(&skey[0],
- ObjectIdAttributeNumber,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(conId));
-
- conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
- SnapshotNow, 1, skey);
-
- tup = systable_getnext(conscan);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "could not find tuple for constraint %u", conId);
+ tup = SearchSysCache(CONSTROID,
+ ObjectIdGetDatum(conId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for constraint %u", conId);
con = (Form_pg_constraint) GETSTRUCT(tup);
/*
@@ -505,97 +564,10 @@ RemoveConstraintById(Oid conId)
simple_heap_delete(conDesc, &tup->t_self);
/* Clean up */
- systable_endscan(conscan);
+ ReleaseSysCache(tup);
heap_close(conDesc, RowExclusiveLock);
}
-/*
- * GetConstraintNameForTrigger
- * Get the name of the constraint owning a trigger, if any
- *
- * Returns a palloc'd string, or NULL if no constraint can be found
- */
-char *
-GetConstraintNameForTrigger(Oid triggerId)
-{
- char *result;
- Oid constraintId = InvalidOid;
- Relation depRel;
- Relation conRel;
- ScanKeyData key[2];
- SysScanDesc scan;
- HeapTuple tup;
-
- /*
- * We must grovel through pg_depend to find the owning constraint. Perhaps
- * pg_trigger should have a column for the owning constraint ... but right
- * now this is not performance-critical code.
- */
- depRel = heap_open(DependRelationId, AccessShareLock);
-
- ScanKeyInit(&key[0],
- Anum_pg_depend_classid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(TriggerRelationId));
- ScanKeyInit(&key[1],
- Anum_pg_depend_objid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(triggerId));
- /* assume we can ignore objsubid for a trigger */
-
- scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
-
- while (HeapTupleIsValid(tup = systable_getnext(scan)))
- {
- Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
-
- if (foundDep->refclassid == ConstraintRelationId &&
- foundDep->deptype == DEPENDENCY_INTERNAL)
- {
- constraintId = foundDep->refobjid;
- break;
- }
- }
-
- systable_endscan(scan);
-
- heap_close(depRel, AccessShareLock);
-
- if (!OidIsValid(constraintId))
- return NULL; /* no owning constraint found */
-
- conRel = heap_open(ConstraintRelationId, AccessShareLock);
-
- ScanKeyInit(&key[0],
- ObjectIdAttributeNumber,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(constraintId));
-
- scan = systable_beginscan(conRel, ConstraintOidIndexId, true,
- SnapshotNow, 1, key);
-
- tup = systable_getnext(scan);
-
- if (HeapTupleIsValid(tup))
- {
- Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
-
- result = pstrdup(NameStr(con->conname));
- }
- else
- {
- /* This arguably should be an error, but we'll just return NULL */
- result = NULL;
- }
-
- systable_endscan(scan);
-
- heap_close(conRel, AccessShareLock);
-
- return result;
-}
-
/*
* AlterConstraintNamespaces
* Find any constraints belonging to the specified object,
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index c11f337eed..6377945285 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.34 2007/01/05 22:19:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.35 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -146,7 +146,7 @@ ConversionDrop(Oid conversionOid, DropBehavior behavior)
HeapTuple tuple;
ObjectAddress object;
- tuple = SearchSysCache(CONOID,
+ tuple = SearchSysCache(CONVOID,
ObjectIdGetDatum(conversionOid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
@@ -313,7 +313,7 @@ pg_convert_using(PG_FUNCTION_ARGS)
errmsg("conversion \"%s\" does not exist",
NameListToString(parsed_name))));
- tuple = SearchSysCache(CONOID,
+ tuple = SearchSysCache(CONVOID,
ObjectIdGetDatum(convoid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 349eadf24f..37cc0e6f57 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.30 2007/01/05 22:19:25 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.31 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -146,7 +146,7 @@ RenameConversion(List *name, const char *newname)
errmsg("conversion \"%s\" does not exist",
NameListToString(name))));
- tup = SearchSysCacheCopy(CONOID,
+ tup = SearchSysCacheCopy(CONVOID,
ObjectIdGetDatum(conversionOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -236,7 +236,7 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
Assert(RelationGetRelid(rel) == ConversionRelationId);
- tup = SearchSysCacheCopy(CONOID,
+ tup = SearchSysCacheCopy(CONVOID,
ObjectIdGetDatum(conversionOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index ae9839641e..9ea32d7707 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.153 2007/01/05 22:19:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.154 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -327,8 +327,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
if (instr->ntuples == 0)
continue;
- if (trig->tgisconstraint &&
- (conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)
+ if (OidIsValid(trig->tgconstraint) &&
+ (conname = get_constraint_name(trig->tgconstraint)) != NULL)
{
appendStringInfo(&buf, "Trigger for constraint %s",
conname);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 001bc57154..cea8ddeabf 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.213 2007/02/02 00:07:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.214 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,6 +28,7 @@
#include "catalog/pg_depend.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/toasting.h"
@@ -139,6 +140,7 @@ typedef struct NewConstraint
char *name; /* Constraint name, or NULL if none */
ConstrType contype; /* CHECK or FOREIGN */
Oid refrelid; /* PK rel, if FOREIGN */
+ Oid conid; /* OID of pg_constraint entry, if FOREIGN */
Node *qual; /* Check expr or FkConstraint struct */
List *qualstate; /* Execution state for CHECK */
} NewConstraint;
@@ -186,10 +188,9 @@ static Oid transformFkeyCheckAttrs(Relation pkrel,
int numattrs, int16 *attnums,
Oid *opclasses);
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
- Relation rel, Relation pkrel);
+ Relation rel, Relation pkrel, Oid constraintOid);
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
- Oid constrOid);
-static char *fkMatchTypeToString(char match_type);
+ Oid constraintOid);
static void ATController(Relation rel, List *cmds, bool recurse);
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
bool recurse, bool recursing);
@@ -256,11 +257,6 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
static void ATExecAddInherit(Relation rel, RangeVar *parent);
static void ATExecDropInherit(Relation rel, RangeVar *parent);
static void copy_relation_data(Relation rel, SMgrRelation dst);
-static void update_ri_trigger_args(Oid relid,
- const char *oldname,
- const char *newname,
- bool fk_scan,
- bool update_relname);
/* ----------------------------------------------------------------
@@ -1615,21 +1611,6 @@ renameatt(Oid myrelid,
heap_close(attrelation, RowExclusiveLock);
- /*
- * Update att name in any RI triggers associated with the relation.
- */
- if (targetrelation->rd_rel->reltriggers > 0)
- {
- /* update tgargs column reference where att is primary key */
- update_ri_trigger_args(RelationGetRelid(targetrelation),
- oldattname, newattname,
- false, false);
- /* update tgargs column reference where att is foreign key */
- update_ri_trigger_args(RelationGetRelid(targetrelation),
- oldattname, newattname,
- true, false);
- }
-
relation_close(targetrelation, NoLock); /* close rel but keep lock */
}
@@ -1708,226 +1689,12 @@ renamerel(Oid myrelid, const char *newrelname)
if (relkind != RELKIND_INDEX)
TypeRename(oldrelname, namespaceId, newrelname);
- /*
- * Update rel name in any RI triggers associated with the relation.
- */
- if (relhastriggers)
- {
- /* update tgargs where relname is primary key */
- update_ri_trigger_args(myrelid,
- oldrelname,
- newrelname,
- false, true);
- /* update tgargs where relname is foreign key */
- update_ri_trigger_args(myrelid,
- oldrelname,
- newrelname,
- true, true);
- }
-
/*
* Close rel, but keep exclusive lock!
*/
relation_close(targetrelation, NoLock);
}
-/*
- * Scan pg_trigger for RI triggers that are on the specified relation
- * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
- * is true). Update RI trigger args fields matching oldname to contain
- * newname instead. If update_relname is true, examine the relname
- * fields; otherwise examine the attname fields.
- */
-static void
-update_ri_trigger_args(Oid relid,
- const char *oldname,
- const char *newname,
- bool fk_scan,
- bool update_relname)
-{
- Relation tgrel;
- ScanKeyData skey[1];
- SysScanDesc trigscan;
- HeapTuple tuple;
- Datum values[Natts_pg_trigger];
- char nulls[Natts_pg_trigger];
- char replaces[Natts_pg_trigger];
-
- tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
- if (fk_scan)
- {
- ScanKeyInit(&skey[0],
- Anum_pg_trigger_tgconstrrelid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(relid));
- trigscan = systable_beginscan(tgrel, TriggerConstrRelidIndexId,
- true, SnapshotNow,
- 1, skey);
- }
- else
- {
- ScanKeyInit(&skey[0],
- Anum_pg_trigger_tgrelid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(relid));
- trigscan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
- true, SnapshotNow,
- 1, skey);
- }
-
- while ((tuple = systable_getnext(trigscan)) != NULL)
- {
- Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
- bytea *val;
- bytea *newtgargs;
- bool isnull;
- int tg_type;
- bool examine_pk;
- bool changed;
- int tgnargs;
- int i;
- int newlen;
- const char *arga[RI_MAX_ARGUMENTS];
- const char *argp;
-
- tg_type = RI_FKey_trigger_type(pg_trigger->tgfoid);
- if (tg_type == RI_TRIGGER_NONE)
- {
- /* Not an RI trigger, forget it */
- continue;
- }
-
- /*
- * It is an RI trigger, so parse the tgargs bytea.
- *
- * NB: we assume the field will never be compressed or moved out of
- * line; so does trigger.c ...
- */
- tgnargs = pg_trigger->tgnargs;
- val = DatumGetByteaP(fastgetattr(tuple,
- Anum_pg_trigger_tgargs,
- tgrel->rd_att, &isnull));
- if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
- tgnargs > RI_MAX_ARGUMENTS)
- {
- /* This probably shouldn't happen, but ignore busted triggers */
- continue;
- }
- argp = (const char *) VARDATA(val);
- for (i = 0; i < tgnargs; i++)
- {
- arga[i] = argp;
- argp += strlen(argp) + 1;
- }
-
- /*
- * Figure out which item(s) to look at. If the trigger is primary-key
- * type and attached to my rel, I should look at the PK fields; if it
- * is foreign-key type and attached to my rel, I should look at the FK
- * fields. But the opposite rule holds when examining triggers found
- * by tgconstrrel search.
- */
- examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
-
- changed = false;
- if (update_relname)
- {
- /* Change the relname if needed */
- i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
- if (strcmp(arga[i], oldname) == 0)
- {
- arga[i] = newname;
- changed = true;
- }
- }
- else
- {
- /* Change attname(s) if needed */
- i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
- RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
- for (; i < tgnargs; i += 2)
- {
- if (strcmp(arga[i], oldname) == 0)
- {
- arga[i] = newname;
- changed = true;
- }
- }
- }
-
- if (!changed)
- {
- /* Don't need to update this tuple */
- continue;
- }
-
- /*
- * Construct modified tgargs bytea.
- */
- newlen = VARHDRSZ;
- for (i = 0; i < tgnargs; i++)
- newlen += strlen(arga[i]) + 1;
- newtgargs = (bytea *) palloc(newlen);
- VARATT_SIZEP(newtgargs) = newlen;
- newlen = VARHDRSZ;
- for (i = 0; i < tgnargs; i++)
- {
- strcpy(((char *) newtgargs) + newlen, arga[i]);
- newlen += strlen(arga[i]) + 1;
- }
-
- /*
- * Build modified tuple.
- */
- for (i = 0; i < Natts_pg_trigger; i++)
- {
- values[i] = (Datum) 0;
- replaces[i] = ' ';
- nulls[i] = ' ';
- }
- values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
- replaces[Anum_pg_trigger_tgargs - 1] = 'r';
-
- tuple = heap_modifytuple(tuple, RelationGetDescr(tgrel), values, nulls, replaces);
-
- /*
- * Update pg_trigger and its indexes
- */
- simple_heap_update(tgrel, &tuple->t_self, tuple);
-
- CatalogUpdateIndexes(tgrel, tuple);
-
- /*
- * Invalidate trigger's relation's relcache entry so that other
- * backends (and this one too!) are sent SI message to make them
- * rebuild relcache entries. (Ideally this should happen
- * automatically...)
- *
- * We can skip this for triggers on relid itself, since that relcache
- * flush will happen anyway due to the table or column rename. We
- * just need to catch the far ends of RI relationships.
- */
- pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
- if (pg_trigger->tgrelid != relid)
- CacheInvalidateRelcacheByRelid(pg_trigger->tgrelid);
-
- /* free up our scratch memory */
- pfree(newtgargs);
- heap_freetuple(tuple);
- }
-
- systable_endscan(trigscan);
-
- heap_close(tgrel, RowExclusiveLock);
-
- /*
- * Increment cmd counter to make updates visible; this is needed in case
- * the same tuple has to be updated again by next pass (can happen in case
- * of a self-referential FK relationship).
- */
- CommandCounterIncrement();
-}
-
/*
* AlterTable
* Execute ALTER TABLE, which can be a list of subcommands
@@ -2552,7 +2319,8 @@ ATRewriteTables(List **wqueue)
refrel = heap_open(con->refrelid, RowShareLock);
- validateForeignKeyConstraint(fkconstraint, rel, refrel);
+ validateForeignKeyConstraint(fkconstraint, rel, refrel,
+ con->conid);
heap_close(refrel, NoLock);
}
@@ -4061,6 +3829,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
+ Oid pfeqoperators[INDEX_MAX_KEYS];
+ Oid ppeqoperators[INDEX_MAX_KEYS];
+ Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
int numfks,
numpks;
@@ -4138,6 +3909,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
+ MemSet(pfeqoperators, 0, sizeof(pfeqoperators));
+ MemSet(ppeqoperators, 0, sizeof(ppeqoperators));
+ MemSet(ffeqoperators, 0, sizeof(ffeqoperators));
numfks = transformColumnNameList(RelationGetRelid(rel),
fkconstraint->fk_attrs,
@@ -4166,7 +3940,15 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
opclasses);
}
- /* Be sure referencing and referenced column types are comparable */
+ /*
+ * Look up the equality operators to use in the constraint.
+ *
+ * Note that we look for operator(s) with the PK type on the left; when
+ * the types are different this is the right choice because the PK index
+ * will need operators with the indexkey on the left. Also, we take the
+ * PK type as being the declared input type of the opclass, which might be
+ * binary-compatible but not identical to the PK column type.
+ */
if (numfks != numpks)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
@@ -4174,24 +3956,71 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
for (i = 0; i < numpks; i++)
{
- /*
- * pktypoid[i] is the primary key table's i'th key's type fktypoid[i]
- * is the foreign key table's i'th key's type
- *
- * Note that we look for an operator with the PK type on the left;
- * when the types are different this is critical because the PK index
- * will need operators with the indexkey on the left. (Ordinarily both
- * commutator operators will exist if either does, but we won't get
- * the right answer from the test below on opclass membership unless
- * we select the proper operator.)
- */
- Operator o = oper(NULL, list_make1(makeString("=")),
- pktypoid[i], fktypoid[i],
- true, -1);
+ HeapTuple cla_ht;
+ Form_pg_opclass cla_tup;
+ Oid amid;
+ Oid opfamily;
+ Oid pktype;
+ Oid fktype;
+ Oid pfeqop;
+ Oid ppeqop;
+ Oid ffeqop;
+ int16 eqstrategy;
- if (o == NULL)
+ /* We need several fields out of the pg_opclass entry */
+ cla_ht = SearchSysCache(CLAOID,
+ ObjectIdGetDatum(opclasses[i]),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ amid = cla_tup->opcmethod;
+ opfamily = cla_tup->opcfamily;
+ pktype = cla_tup->opcintype;
+ ReleaseSysCache(cla_ht);
+
+ /*
+ * Check it's a btree; currently this can never fail since no other
+ * index AMs support unique indexes. If we ever did have other
+ * types of unique indexes, we'd need a way to determine which
+ * operator strategy number is equality. (Is it reasonable to
+ * insist that every such index AM use btree's number for equality?)
+ */
+ if (amid != BTREE_AM_OID)
+ elog(ERROR, "only b-tree indexes are supported for foreign keys");
+ eqstrategy = BTEqualStrategyNumber;
+
+ /*
+ * There had better be a PK = PK operator for the index.
+ */
+ ppeqop = get_opfamily_member(opfamily, pktype, pktype, eqstrategy);
+
+ if (!OidIsValid(ppeqop))
+ elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+ eqstrategy, pktype, pktype, opfamily);
+
+ /*
+ * Are there equality operators that take exactly the FK type?
+ * Assume we should look through any domain here.
+ */
+ fktype = getBaseType(fktypoid[i]);
+
+ pfeqop = get_opfamily_member(opfamily, pktype, fktype, eqstrategy);
+ ffeqop = get_opfamily_member(opfamily, fktype, fktype, eqstrategy);
+
+ if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
+ {
+ /*
+ * Otherwise, look for an implicit cast from the FK type to
+ * the PK type, and if found, use the PK type's equality operator.
+ */
+ if (can_coerce_type(1, &fktype, &pktype, COERCION_IMPLICIT))
+ pfeqop = ffeqop = ppeqop;
+ }
+
+ if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("foreign key constraint \"%s\" "
"cannot be implemented",
fkconstraint->constr_name),
@@ -4202,41 +4031,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
format_type_be(fktypoid[i]),
format_type_be(pktypoid[i]))));
- /*
- * Check that the found operator is compatible with the PK index, and
- * generate a warning if not, since otherwise costly seqscans will be
- * incurred to check FK validity.
- */
- if (!op_in_opfamily(oprid(o), get_opclass_family(opclasses[i])))
- ereport(WARNING,
- (errmsg("foreign key constraint \"%s\" "
- "will require costly sequential scans",
- fkconstraint->constr_name),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of different types: %s and %s.",
- strVal(list_nth(fkconstraint->fk_attrs, i)),
- strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktypoid[i]),
- format_type_be(pktypoid[i]))));
-
- ReleaseSysCache(o);
- }
-
- /*
- * Tell Phase 3 to check that the constraint is satisfied by existing rows
- * (we can skip this during table creation).
- */
- if (!fkconstraint->skip_validation)
- {
- NewConstraint *newcon;
-
- newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
- newcon->name = fkconstraint->constr_name;
- newcon->contype = CONSTR_FOREIGN;
- newcon->refrelid = RelationGetRelid(pkrel);
- newcon->qual = (Node *) fkconstraint;
-
- tab->constraints = lappend(tab->constraints, newcon);
+ pfeqoperators[i] = pfeqop;
+ ppeqoperators[i] = ppeqop;
+ ffeqoperators[i] = ffeqop;
}
/*
@@ -4254,6 +4051,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* constraint */
RelationGetRelid(pkrel),
pkattnum,
+ pfeqoperators,
+ ppeqoperators,
+ ffeqoperators,
numpks,
fkconstraint->fk_upd_action,
fkconstraint->fk_del_action,
@@ -4268,6 +4068,24 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
createForeignKeyTriggers(rel, fkconstraint, constrOid);
+ /*
+ * Tell Phase 3 to check that the constraint is satisfied by existing rows
+ * (we can skip this during table creation).
+ */
+ if (!fkconstraint->skip_validation)
+ {
+ NewConstraint *newcon;
+
+ newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
+ newcon->name = fkconstraint->constr_name;
+ newcon->contype = CONSTR_FOREIGN;
+ newcon->refrelid = RelationGetRelid(pkrel);
+ newcon->conid = constrOid;
+ newcon->qual = (Node *) fkconstraint;
+
+ tab->constraints = lappend(tab->constraints, newcon);
+ }
+
/*
* Close pk table, but keep lock until we've committed.
*/
@@ -4526,25 +4344,15 @@ transformFkeyCheckAttrs(Relation pkrel,
static void
validateForeignKeyConstraint(FkConstraint *fkconstraint,
Relation rel,
- Relation pkrel)
+ Relation pkrel,
+ Oid constraintOid)
{
HeapScanDesc scan;
HeapTuple tuple;
Trigger trig;
- ListCell *list;
- int count;
/*
- * See if we can do it with a single LEFT JOIN query. A FALSE result
- * indicates we must proceed with the fire-the-trigger method.
- */
- if (RI_Initial_Check(fkconstraint, rel, pkrel))
- return;
-
- /*
- * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
- * if that tuple had just been inserted. If any of those fail, it should
- * ereport(ERROR) and that's that.
+ * Build a trigger call structure; we'll need it either way.
*/
MemSet(&trig, 0, sizeof(trig));
trig.tgoid = InvalidOid;
@@ -4552,35 +4360,23 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
trig.tgenabled = TRUE;
trig.tgisconstraint = TRUE;
trig.tgconstrrelid = RelationGetRelid(pkrel);
+ trig.tgconstraint = constraintOid;
trig.tgdeferrable = FALSE;
trig.tginitdeferred = FALSE;
+ /* we needn't fill in tgargs */
- trig.tgargs = (char **) palloc(sizeof(char *) *
- (4 + list_length(fkconstraint->fk_attrs)
- + list_length(fkconstraint->pk_attrs)));
-
- trig.tgargs[0] = trig.tgname;
- trig.tgargs[1] = RelationGetRelationName(rel);
- trig.tgargs[2] = RelationGetRelationName(pkrel);
- trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype);
- count = 4;
- foreach(list, fkconstraint->fk_attrs)
- {
- char *fk_at = strVal(lfirst(list));
-
- trig.tgargs[count] = fk_at;
- count += 2;
- }
- count = 5;
- foreach(list, fkconstraint->pk_attrs)
- {
- char *pk_at = strVal(lfirst(list));
-
- trig.tgargs[count] = pk_at;
- count += 2;
- }
- trig.tgnargs = count - 1;
+ /*
+ * See if we can do it with a single LEFT JOIN query. A FALSE result
+ * indicates we must proceed with the fire-the-trigger method.
+ */
+ if (RI_Initial_Check(&trig, rel, pkrel))
+ return;
+ /*
+ * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
+ * if that tuple had just been inserted. If any of those fail, it should
+ * ereport(ERROR) and that's that.
+ */
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
@@ -4613,18 +4409,13 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint,
}
heap_endscan(scan);
-
- pfree(trig.tgargs);
}
static void
CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
- ObjectAddress *constrobj, ObjectAddress *trigobj,
- bool on_insert)
+ Oid constraintOid, bool on_insert)
{
CreateTrigStmt *fk_trigger;
- ListCell *fk_attr;
- ListCell *pk_attr;
fk_trigger = makeNode(CreateTrigStmt);
fk_trigger->trigname = fkconstraint->constr_name;
@@ -4649,32 +4440,9 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->constrrel = fkconstraint->pktable;
-
fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(myRel->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
- if (list_length(fkconstraint->fk_attrs) != list_length(fkconstraint->pk_attrs))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_FOREIGN_KEY),
- errmsg("number of referencing and referenced columns for foreign key disagree")));
- forboth(fk_attr, fkconstraint->fk_attrs,
- pk_attr, fkconstraint->pk_attrs)
- {
- fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr));
- fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr));
- }
-
- trigobj->objectId = CreateTrigger(fk_trigger, true);
-
- /* Register dependency from trigger to constraint */
- recordDependencyOn(trigobj, constrobj, DEPENDENCY_INTERNAL);
+ (void) CreateTrigger(fk_trigger, constraintOid);
/* Make changes-so-far visible */
CommandCounterIncrement();
@@ -4685,14 +4453,10 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint,
*/
static void
createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
- Oid constrOid)
+ Oid constraintOid)
{
RangeVar *myRel;
CreateTrigStmt *fk_trigger;
- ListCell *fk_attr;
- ListCell *pk_attr;
- ObjectAddress trigobj,
- constrobj;
/*
* Reconstruct a RangeVar for my relation (not passed in, unfortunately).
@@ -4700,15 +4464,6 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
pstrdup(RelationGetRelationName(rel)));
- /*
- * Preset objectAddress fields
- */
- constrobj.classId = ConstraintRelationId;
- constrobj.objectId = constrOid;
- constrobj.objectSubId = 0;
- trigobj.classId = TriggerRelationId;
- trigobj.objectSubId = 0;
-
/* Make changes-so-far visible */
CommandCounterIncrement();
@@ -4716,8 +4471,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK
* action for both INSERTs and UPDATEs on the referencing table.
*/
- CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, true);
- CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, false);
+ CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, true);
+ CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, false);
/*
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
@@ -4765,27 +4520,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
(int) fkconstraint->fk_del_action);
break;
}
-
fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(myRel->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
- forboth(fk_attr, fkconstraint->fk_attrs,
- pk_attr, fkconstraint->pk_attrs)
- {
- fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr));
- fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr));
- }
- trigobj.objectId = CreateTrigger(fk_trigger, true);
-
- /* Register dependency from trigger to constraint */
- recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
+ (void) CreateTrigger(fk_trigger, constraintOid);
/* Make changes-so-far visible */
CommandCounterIncrement();
@@ -4835,49 +4572,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
(int) fkconstraint->fk_upd_action);
break;
}
-
fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(myRel->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
- forboth(fk_attr, fkconstraint->fk_attrs,
- pk_attr, fkconstraint->pk_attrs)
- {
- fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr));
- fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr));
- }
- trigobj.objectId = CreateTrigger(fk_trigger, true);
-
- /* Register dependency from trigger to constraint */
- recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
-}
-
-/*
- * fkMatchTypeToString -
- * convert FKCONSTR_MATCH_xxx code to string to use in trigger args
- */
-static char *
-fkMatchTypeToString(char match_type)
-{
- switch (match_type)
- {
- case FKCONSTR_MATCH_FULL:
- return pstrdup("FULL");
- case FKCONSTR_MATCH_PARTIAL:
- return pstrdup("PARTIAL");
- case FKCONSTR_MATCH_UNSPECIFIED:
- return pstrdup("UNSPECIFIED");
- default:
- elog(ERROR, "unrecognized match type: %d",
- (int) match_type);
- }
- return NULL; /* can't get here */
+ (void) CreateTrigger(fk_trigger, constraintOid);
}
/*
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index cc7dfc895b..c08525c2e0 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.212 2007/01/25 04:17:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.213 2007/02/14 01:58:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,6 +19,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -57,13 +58,12 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
/*
* Create a trigger. Returns the OID of the created trigger.
*
- * forConstraint, if true, says that this trigger is being created to
- * implement a constraint. The caller will then be expected to make
- * a pg_depend entry linking the trigger to that constraint (and thereby
- * to the owning relation(s)).
+ * constraintOid, if nonzero, says that this trigger is being created to
+ * implement that constraint. A suitable pg_depend entry will be made
+ * to link the trigger to that constraint.
*/
Oid
-CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
+CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid)
{
int16 tgtype;
int2vector *tgattr;
@@ -91,51 +91,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
- if (stmt->constrrel != NULL)
- constrrelid = RangeVarGetRelid(stmt->constrrel, false);
- else if (stmt->isconstraint)
- {
- /*
- * If this trigger is a constraint (and a foreign key one) then we
- * really need a constrrelid. Since we don't have one, we'll try to
- * generate one from the argument information.
- *
- * This is really just a workaround for a long-ago pg_dump bug that
- * omitted the FROM clause in dumped CREATE CONSTRAINT TRIGGER
- * commands. We don't want to bomb out completely here if we can't
- * determine the correct relation, because that would prevent loading
- * the dump file. Instead, NOTICE here and ERROR in the trigger.
- */
- bool needconstrrelid = false;
- void *elem = NULL;
-
- if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_check_", 14) == 0)
- {
- /* A trigger on FK table. */
- needconstrrelid = true;
- if (list_length(stmt->args) > RI_PK_RELNAME_ARGNO)
- elem = list_nth(stmt->args, RI_PK_RELNAME_ARGNO);
- }
- else if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_", 8) == 0)
- {
- /* A trigger on PK table. */
- needconstrrelid = true;
- if (list_length(stmt->args) > RI_FK_RELNAME_ARGNO)
- elem = list_nth(stmt->args, RI_FK_RELNAME_ARGNO);
- }
- if (elem != NULL)
- {
- RangeVar *rel = makeRangeVar(NULL, strVal(elem));
-
- constrrelid = RangeVarGetRelid(rel, true);
- }
- if (needconstrrelid && constrrelid == InvalidOid)
- ereport(NOTICE,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("could not determine referenced table for constraint \"%s\"",
- stmt->trigname)));
- }
-
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -152,15 +107,17 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
if (stmt->isconstraint)
{
- /* foreign key constraint trigger */
-
+ /* constraint trigger */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
ACL_REFERENCES);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
- if (constrrelid != InvalidOid)
+
+ if (stmt->constrrel != NULL)
{
+ constrrelid = RangeVarGetRelid(stmt->constrrel, false);
+
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
ACL_REFERENCES);
if (aclresult != ACLCHECK_OK)
@@ -170,7 +127,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
}
else
{
- /* real trigger */
+ /* regular trigger */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
ACL_TRIGGER);
if (aclresult != ACLCHECK_OK)
@@ -187,23 +144,31 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
trigoid = GetNewOid(tgrel);
/*
- * If trigger is an RI constraint, use specified trigger name as
- * constraint name and build a unique trigger name instead. This is mainly
- * for backwards compatibility with CREATE CONSTRAINT TRIGGER commands.
+ * If trigger is for an RI constraint, the passed-in name is the
+ * constraint name; save that and build a unique trigger name to avoid
+ * collisions with user-selected trigger names.
*/
- if (stmt->isconstraint)
+ if (OidIsValid(constraintOid))
{
snprintf(constrtrigname, sizeof(constrtrigname),
"RI_ConstraintTrigger_%u", trigoid);
trigname = constrtrigname;
constrname = stmt->trigname;
}
+ else if (stmt->isconstraint)
+ {
+ /* constraint trigger: trigger name is also constraint name */
+ trigname = stmt->trigname;
+ constrname = stmt->trigname;
+ }
else
{
+ /* regular trigger: use empty constraint name */
trigname = stmt->trigname;
constrname = "";
}
+ /* Compute tgtype */
TRIGGER_CLEAR_TYPE(tgtype);
if (stmt->before)
TRIGGER_SETT_BEFORE(tgtype);
@@ -298,7 +263,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
/*
* Build the new pg_trigger tuple.
*/
- MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));
+ memset(nulls, ' ', Natts_pg_trigger * sizeof(char));
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
@@ -310,6 +275,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
CStringGetDatum(constrname));
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
+ values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
@@ -372,10 +338,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
CatalogUpdateIndexes(tgrel, tuple);
- myself.classId = TriggerRelationId;
- myself.objectId = trigoid;
- myself.objectSubId = 0;
-
heap_freetuple(tuple);
heap_close(tgrel, RowExclusiveLock);
@@ -412,20 +374,36 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
/*
* Record dependencies for trigger. Always place a normal dependency on
- * the function. If we are doing this in response to an explicit CREATE
- * TRIGGER command, also make trigger be auto-dropped if its relation is
- * dropped or if the FK relation is dropped. (Auto drop is compatible
- * with our pre-7.3 behavior.) If the trigger is being made for a
- * constraint, we can skip the relation links; the dependency on the
- * constraint will indirectly depend on the relations.
+ * the function.
*/
+ myself.classId = TriggerRelationId;
+ myself.objectId = trigoid;
+ myself.objectSubId = 0;
+
referenced.classId = ProcedureRelationId;
referenced.objectId = funcoid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- if (!forConstraint)
+ if (OidIsValid(constraintOid))
{
+ /*
+ * It's for a constraint, so make it an internal dependency of the
+ * constraint. We can skip depending on the relations, as there'll
+ * be an indirect dependency via the constraint.
+ */
+ referenced.classId = ConstraintRelationId;
+ referenced.objectId = constraintOid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ }
+ else
+ {
+ /*
+ * Regular CREATE TRIGGER, so place dependencies. We make trigger be
+ * auto-dropped if its relation is dropped or if the FK relation is
+ * dropped. (Auto drop is compatible with our pre-7.3 behavior.)
+ */
referenced.classId = RelationRelationId;
referenced.objectId = RelationGetRelid(rel);
referenced.objectSubId = 0;
@@ -773,7 +751,7 @@ EnableDisableTrigger(Relation rel, const char *tgname,
{
Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
- if (oldtrig->tgisconstraint)
+ if (OidIsValid(oldtrig->tgconstraint))
{
/* system trigger ... ok to process? */
if (skip_system)
@@ -886,6 +864,7 @@ RelationBuildTriggers(Relation relation)
build->tgenabled = pg_trigger->tgenabled;
build->tgisconstraint = pg_trigger->tgisconstraint;
build->tgconstrrelid = pg_trigger->tgconstrrelid;
+ build->tgconstraint = pg_trigger->tgconstraint;
build->tgdeferrable = pg_trigger->tgdeferrable;
build->tginitdeferred = pg_trigger->tginitdeferred;
build->tgnargs = pg_trigger->tgnargs;
@@ -1220,6 +1199,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
return false;
if (trig1->tgconstrrelid != trig2->tgconstrrelid)
return false;
+ if (trig1->tgconstraint != trig2->tgconstraint)
+ return false;
if (trig1->tgdeferrable != trig2->tgdeferrable)
return false;
if (trig1->tginitdeferred != trig2->tginitdeferred)
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 4f88e9561c..99f964db74 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.99 2007/01/05 22:19:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.100 2007/02/14 01:58:57 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@@ -1966,6 +1966,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
domainOid, /* domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
+ NULL,
+ NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 613ef653d1..5cabec2970 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.271 2007/01/23 05:07:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.272 2007/02/14 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -973,7 +973,7 @@ ProcessUtility(Node *parsetree,
break;
case T_CreateTrigStmt:
- CreateTrigger((CreateTrigStmt *) parsetree, false);
+ CreateTrigger((CreateTrigStmt *) parsetree, InvalidOid);
break;
case T_DropPropertyStmt:
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c21d827d0c..4ba2bb7a98 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -17,7 +17,7 @@
*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.90 2007/01/05 22:19:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.91 2007/02/14 01:58:57 tgl Exp $
*
* ----------
*/
@@ -32,13 +32,17 @@
#include "postgres.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_operator.h"
#include "commands/trigger.h"
#include "executor/spi_priv.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_relation.h"
+#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
-#include "utils/typcache.h"
-#include "miscadmin.h"
+#include "utils/memutils.h"
/* ----------
@@ -46,11 +50,9 @@
* ----------
*/
-#define RI_INIT_QUERYHASHSIZE 128
+#define RI_MAX_NUMKEYS INDEX_MAX_KEYS
-#define RI_MATCH_TYPE_UNSPECIFIED 0
-#define RI_MATCH_TYPE_FULL 1
-#define RI_MATCH_TYPE_PARTIAL 2
+#define RI_INIT_QUERYHASHSIZE 128
#define RI_KEYS_ALL_NULL 0
#define RI_KEYS_SOME_NULL 1
@@ -72,21 +74,51 @@
#define MAX_QUOTED_NAME_LEN (NAMEDATALEN*2+3)
#define MAX_QUOTED_REL_NAME_LEN (MAX_QUOTED_NAME_LEN*2)
+#define RIAttName(rel, attnum) NameStr(*attnumAttName(rel, attnum))
+#define RIAttType(rel, attnum) SPI_gettypeid(RelationGetDescr(rel), attnum)
+
#define RI_TRIGTYPE_INSERT 1
#define RI_TRIGTYPE_UPDATE 2
#define RI_TRIGTYPE_INUP 3
#define RI_TRIGTYPE_DELETE 4
+#define RI_KEYPAIR_FK_IDX 0
+#define RI_KEYPAIR_PK_IDX 1
+
+
+/* ----------
+ * RI_ConstraintInfo
+ *
+ * Information extracted from an FK pg_constraint entry.
+ * ----------
+ */
+typedef struct RI_ConstraintInfo
+{
+ Oid constraint_id; /* OID of pg_constraint entry */
+ NameData conname; /* name of the FK constraint */
+ Oid pk_relid; /* referenced relation */
+ Oid fk_relid; /* referencing relation */
+ char confupdtype; /* foreign key's ON UPDATE action */
+ char confdeltype; /* foreign key's ON DELETE action */
+ char confmatchtype; /* foreign key's match type */
+ int nkeys; /* number of key columns */
+ int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
+ int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
+ Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
+ Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
+} RI_ConstraintInfo;
+
/* ----------
* RI_QueryKey
*
- * The key identifying a prepared SPI plan in our private hashtable
+ * The key identifying a prepared SPI plan in our query hashtable
* ----------
*/
typedef struct RI_QueryKey
{
- int32 constr_type;
+ char constr_type;
Oid constr_id;
int32 constr_queryno;
Oid fk_relid;
@@ -107,11 +139,38 @@ typedef struct RI_QueryHashEntry
} RI_QueryHashEntry;
+/* ----------
+ * RI_CompareKey
+ *
+ * The key identifying an entry showing how to compare two values
+ * ----------
+ */
+typedef struct RI_CompareKey
+{
+ Oid eq_opr; /* the equality operator to apply */
+ Oid typeid; /* the data type to apply it to */
+} RI_CompareKey;
+
+
+/* ----------
+ * RI_CompareHashEntry
+ * ----------
+ */
+typedef struct RI_CompareHashEntry
+{
+ RI_CompareKey key;
+ bool valid; /* successfully initialized? */
+ FmgrInfo eq_opr_finfo; /* call info for equality fn */
+ FmgrInfo cast_func_finfo; /* in case we must coerce input */
+} RI_CompareHashEntry;
+
+
/* ----------
* Local data
* ----------
*/
static HTAB *ri_query_cache = NULL;
+static HTAB *ri_compare_cache = NULL;
/* ----------
@@ -120,35 +179,41 @@ static HTAB *ri_query_cache = NULL;
*/
static void quoteOneName(char *buffer, const char *name);
static void quoteRelationName(char *buffer, Relation rel);
-static int ri_DetermineMatchType(char *str);
+static void ri_GenerateQual(StringInfo buf,
+ const char *sep,
+ const char *leftop, Oid leftoptype,
+ Oid opoid,
+ const char *rightop, Oid rightoptype);
static int ri_NullCheck(Relation rel, HeapTuple tup,
RI_QueryKey *key, int pairidx);
-static void ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id,
- int32 constr_queryno,
- Relation fk_rel, Relation pk_rel,
- int argc, char **argv);
-static void ri_BuildQueryKeyPkCheck(RI_QueryKey *key, Oid constr_id,
- int32 constr_queryno,
- Relation pk_rel,
- int argc, char **argv);
+static void ri_BuildQueryKeyFull(RI_QueryKey *key,
+ const RI_ConstraintInfo *riinfo,
+ int32 constr_queryno);
+static void ri_BuildQueryKeyPkCheck(RI_QueryKey *key,
+ const RI_ConstraintInfo *riinfo,
+ int32 constr_queryno);
static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
- RI_QueryKey *key, int pairidx);
+ const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
- RI_QueryKey *key, int pairidx);
-static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
- HeapTuple newtup, RI_QueryKey *key, int pairidx);
-static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
+ const RI_ConstraintInfo *riinfo, bool rel_is_pk);
+static bool ri_OneKeyEqual(Relation rel, int column,
+ HeapTuple oldtup, HeapTuple newtup,
+ const RI_ConstraintInfo *riinfo, bool rel_is_pk);
+static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
+ Datum oldvalue, Datum newvalue);
static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
HeapTuple old_row,
- Oid tgoid, int match_type,
- int tgnargs, char **tgargs);
+ const RI_ConstraintInfo *riinfo);
static void ri_InitHashTables(void);
static void *ri_FetchPreparedPlan(RI_QueryKey *key);
static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);
+static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
int tgkind);
+static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
+ Trigger *trigger, Relation trig_rel, bool rel_is_pk);
static void *ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
bool cache_plan);
@@ -176,8 +241,7 @@ static Datum
RI_FKey_check(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple new_row;
@@ -186,7 +250,6 @@ RI_FKey_check(PG_FUNCTION_ARGS)
RI_QueryKey qkey;
void *qplan;
int i;
- int match_type;
/*
* Check that this is a valid trigger call on the right time and event.
@@ -196,8 +259,9 @@ RI_FKey_check(PG_FUNCTION_ARGS)
/*
* Get arguments.
*/
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, false);
+
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
{
old_row = trigdata->tg_trigtuple;
@@ -237,7 +301,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
* SELECT FOR SHARE will get on it.
*/
fk_rel = trigdata->tg_relation;
- pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
+ pk_rel = heap_open(riinfo.pk_relid, RowShareLock);
/* ----------
* SQL3 11.9
@@ -250,12 +314,9 @@ RI_FKey_check(PG_FUNCTION_ARGS)
* future enhancements.
* ----------
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
{
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_CHECK_LOOKUPPK_NOCOLS,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK_NOCOLS);
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "SPI_connect failed");
@@ -271,7 +332,8 @@ RI_FKey_check(PG_FUNCTION_ARGS)
* ----------
*/
quoteRelationName(pkrelname, pk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x FOR SHARE OF x",
+ snprintf(querystr, sizeof(querystr),
+ "SELECT 1 FROM ONLY %s x FOR SHARE OF x",
pkrelname);
/* Prepare and save the plan */
@@ -287,7 +349,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
NULL, NULL,
false,
SPI_OK_SELECT,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -295,19 +357,14 @@ RI_FKey_check(PG_FUNCTION_ARGS)
heap_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
-
}
- match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
-
- if (match_type == RI_MATCH_TYPE_PARTIAL)
+ if (riinfo.confmatchtype == FKCONSTR_MATCH_PARTIAL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_CHECK_LOOKUPPK, fk_rel, pk_rel,
- tgnargs, tgargs);
+ ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
switch (ri_NullCheck(fk_rel, new_row, &qkey, RI_KEYPAIR_FK_IDX))
{
@@ -329,9 +386,9 @@ RI_FKey_check(PG_FUNCTION_ARGS)
* This is the only case that differs between the three kinds of
* MATCH.
*/
- switch (match_type)
+ switch (riinfo.confmatchtype)
{
- case RI_MATCH_TYPE_FULL:
+ case FKCONSTR_MATCH_FULL:
/*
* Not allowed - MATCH FULL says either all or none of the
@@ -341,12 +398,12 @@ RI_FKey_check(PG_FUNCTION_ARGS)
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
RelationGetRelationName(trigdata->tg_relation),
- tgargs[RI_CONSTRAINT_NAME_ARGNO]),
+ NameStr(riinfo.conname)),
errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
heap_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
- case RI_MATCH_TYPE_UNSPECIFIED:
+ case FKCONSTR_MATCH_UNSPECIFIED:
/*
* MATCH - if ANY column is null, we have a
@@ -355,7 +412,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
heap_close(pk_rel, RowShareLock);
return PointerGetDatum(NULL);
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
/*
* MATCH PARTIAL - all non-null columns must match. (not
@@ -387,39 +444,43 @@ RI_FKey_check(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * SELECT 1 FROM ONLY WHERE pkatt1 = $1 [AND ...]
+ * SELECT 1 FROM ONLY WHERE pkatt1 = $1 [AND ...] FOR SHARE
* The type id's for the $ parameters are those of the
- * corresponding FK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding FK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", pkrelname);
+ appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_PK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(pk_rel, riinfo.pk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ attname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ paramname, fk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(fk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_FK_IDX]);
+ queryoids[i] = fk_type;
}
- strcat(querystr, " FOR SHARE OF x");
+ appendStringInfo(&querybuf, " FOR SHARE OF x");
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -431,7 +492,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
NULL, new_row,
false,
SPI_OK_SELECT,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -471,7 +532,7 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
/* ----------
* ri_Check_Pk_Match
*
- * Check for matching value of old pk row in current state for
+ * Check for matching value of old pk row in current state for
* noaction triggers. Returns false if no row was found and a fk row
* could potentially be referencing this row, true otherwise.
* ----------
@@ -479,17 +540,14 @@ RI_FKey_check_upd(PG_FUNCTION_ARGS)
static bool
ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
HeapTuple old_row,
- Oid tgoid, int match_type,
- int tgnargs, char **tgargs)
+ const RI_ConstraintInfo *riinfo)
{
void *qplan;
RI_QueryKey qkey;
int i;
bool result;
- ri_BuildQueryKeyPkCheck(&qkey, tgoid,
- RI_PLAN_CHECK_LOOKUPPK, pk_rel,
- tgnargs, tgargs);
+ ri_BuildQueryKeyPkCheck(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -506,10 +564,10 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
* This is the only case that differs between the three kinds of
* MATCH.
*/
- switch (match_type)
+ switch (riinfo->confmatchtype)
{
- case RI_MATCH_TYPE_FULL:
- case RI_MATCH_TYPE_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ case FKCONSTR_MATCH_UNSPECIFIED:
/*
* MATCH /FULL - if ANY column is null, we
@@ -517,7 +575,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
*/
return true;
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
/*
* MATCH PARTIAL - all non-null columns must match. (not
@@ -548,39 +606,42 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * SELECT 1 FROM ONLY WHERE pkatt1 = $1 [AND ...]
+ * SELECT 1 FROM ONLY WHERE pkatt1 = $1 [AND ...] FOR SHARE
* The type id's for the $ parameters are those of the
- * corresponding FK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * PK attributes themselves.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", pkrelname);
+ appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo->nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_PK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(pk_rel, riinfo->pk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ attname, pk_type,
+ riinfo->pp_eq_oprs[i],
+ paramname, pk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, " FOR SHARE OF x");
+ appendStringInfo(&querybuf, " FOR SHARE OF x");
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -612,28 +673,29 @@ Datum
RI_FKey_noaction_del(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple old_row;
RI_QueryKey qkey;
void *qplan;
int i;
- int match_type;
/*
* Check that this is a valid trigger call on the right time and event.
*/
ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -642,14 +704,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
* fk_rel is opened in RowShareLock mode since that's what our eventual
* SELECT FOR SHARE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple;
- match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
- if (ri_Check_Pk_Match(pk_rel, fk_rel,
- old_row, trigdata->tg_trigger->tgoid,
- match_type, tgnargs, tgargs))
+ if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))
{
/*
* There's either another row, or no row could match this one. In
@@ -659,7 +718,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
return PointerGetDatum(NULL);
}
- switch (match_type)
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -668,12 +727,10 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
* ... ON DELETE CASCADE
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_NOACTION_DEL_CHECKREF,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_NOACTION_DEL_CHECKREF);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -704,39 +761,44 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * SELECT 1 FROM ONLY WHERE fkatt1 = $1 [AND ...]
+ * SELECT 1 FROM ONLY WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", fkrelname);
+ appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
+ fkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, " FOR SHARE OF x");
+ appendStringInfo(&querybuf, " FOR SHARE OF x");
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -748,7 +810,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_SELECT,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -760,7 +822,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL restrict delete.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -770,7 +832,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -787,8 +849,7 @@ Datum
RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple new_row;
@@ -796,20 +857,22 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
RI_QueryKey qkey;
void *qplan;
int i;
- int match_type;
/*
* Check that this is a valid trigger call on the right time and event.
*/
ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -819,14 +882,12 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
* fk_rel is opened in RowShareLock mode since that's what our eventual
* SELECT FOR SHARE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
new_row = trigdata->tg_newtuple;
old_row = trigdata->tg_trigtuple;
- match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
-
- switch (match_type)
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -835,12 +896,10 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
* ... ON DELETE CASCADE
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_NOACTION_UPD_CHECKREF,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_NOACTION_UPD_CHECKREF);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -865,16 +924,13 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
/*
* No need to check anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX))
+ if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
{
heap_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
}
- if (ri_Check_Pk_Match(pk_rel, fk_rel,
- old_row, trigdata->tg_trigger->tgoid,
- match_type, tgnargs, tgargs))
+ if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))
{
/*
* There's either another row, or no row could match this one.
@@ -893,39 +949,44 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * SELECT 1 FROM ONLY WHERE fkatt1 = $1 [AND ...]
+ * SELECT 1 FROM ONLY WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", fkrelname);
+ appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
+ fkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, " FOR SHARE OF x");
+ appendStringInfo(&querybuf, " FOR SHARE OF x");
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -937,7 +998,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_SELECT,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -949,7 +1010,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL noaction update.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -959,7 +1020,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -974,8 +1035,7 @@ Datum
RI_FKey_cascade_del(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple old_row;
@@ -988,13 +1048,16 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -1003,11 +1066,11 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
* fk_rel is opened in RowExclusiveLock mode since that's what our
* eventual DELETE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple;
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -1016,12 +1079,10 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
* ... ON DELETE CASCADE
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_CASCADE_DEL_DODELETE,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_CASCADE_DEL_DODELETE);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -1051,38 +1112,42 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * DELETE FROM ONLY WHERE fkatt1 = $1 [AND ...]
+ * DELETE FROM ONLY WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "DELETE FROM ONLY %s", fkrelname);
+ appendStringInfo(&querybuf, "DELETE FROM ONLY %s", fkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -1095,7 +1160,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_DELETE,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -1107,7 +1172,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL cascaded delete.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -1117,7 +1182,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -1132,8 +1197,7 @@ Datum
RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple new_row;
@@ -1148,13 +1212,16 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -1164,12 +1231,12 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
* fk_rel is opened in RowExclusiveLock mode since that's what our
* eventual UPDATE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
new_row = trigdata->tg_newtuple;
old_row = trigdata->tg_trigtuple;
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -1178,12 +1245,10 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
* ... ON UPDATE CASCADE
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_CASCADE_UPD_DOUPDATE,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_CASCADE_UPD_DOUPDATE);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -1208,8 +1273,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
/*
* No need to do anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX))
+ if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
{
heap_close(fk_rel, RowExclusiveLock);
return PointerGetDatum(NULL);
@@ -1224,11 +1288,11 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
- char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
const char *qualsep;
Oid queryoids[RI_MAX_NUMKEYS * 2];
@@ -1236,36 +1300,43 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
/* ----------
* The query string built is
* UPDATE ONLY SET fkatt1 = $1 [, ...]
- * WHERE fkatt1 = $n [AND ...]
+ * WHERE $n = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes. Note that we are assuming
+ * there is an assignment cast from the PK to the FK type;
+ * else the parser will fail.
* ----------
*/
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname);
- qualstr[0] = '\0';
+ appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
querysep = "";
qualsep = "WHERE";
- for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)
+ for (i = 0, j = riinfo.nkeys; i < riinfo.nkeys; i++, j++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = $%d",
- querysep, attname, i + 1);
- snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d",
- qualsep, attname, j + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ appendStringInfo(&querybuf,
+ "%s %s = $%d",
+ querysep, attname, i + 1);
+ sprintf(paramname, "$%d", j + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = ",";
qualsep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
- queryoids[j] = queryoids[i];
+ queryoids[i] = pk_type;
+ queryoids[j] = pk_type;
}
- strcat(querystr, qualstr);
+ appendStringInfoString(&querybuf, qualbuf.data);
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs * 2, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys * 2, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -1277,7 +1348,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
old_row, new_row,
true, /* must detect new rows */
SPI_OK_UPDATE,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -1289,7 +1360,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL cascade update.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -1299,7 +1370,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -1321,8 +1392,7 @@ Datum
RI_FKey_restrict_del(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple old_row;
@@ -1335,13 +1405,16 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -1350,11 +1423,11 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
* fk_rel is opened in RowShareLock mode since that's what our eventual
* SELECT FOR SHARE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple;
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -1363,12 +1436,10 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
* ... ON DELETE CASCADE
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_RESTRICT_DEL_CHECKREF,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_RESTRICT_DEL_CHECKREF);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -1399,39 +1470,44 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * SELECT 1 FROM ONLY WHERE fkatt1 = $1 [AND ...]
+ * SELECT 1 FROM ONLY WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", fkrelname);
+ appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
+ fkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, " FOR SHARE OF x");
+ appendStringInfo(&querybuf, " FOR SHARE OF x");
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -1443,7 +1519,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_SELECT,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -1455,7 +1531,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL restrict delete.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -1465,7 +1541,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -1487,8 +1563,7 @@ Datum
RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple new_row;
@@ -1502,13 +1577,16 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -1518,12 +1596,12 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
* fk_rel is opened in RowShareLock mode since that's what our eventual
* SELECT FOR SHARE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
pk_rel = trigdata->tg_relation;
new_row = trigdata->tg_newtuple;
old_row = trigdata->tg_trigtuple;
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -1532,12 +1610,10 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
* ... ON DELETE CASCADE
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_RESTRICT_UPD_CHECKREF,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_RESTRICT_UPD_CHECKREF);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -1562,8 +1638,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
/*
* No need to check anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX))
+ if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
{
heap_close(fk_rel, RowShareLock);
return PointerGetDatum(NULL);
@@ -1578,39 +1653,44 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
Oid queryoids[RI_MAX_NUMKEYS];
/* ----------
* The query string built is
- * SELECT 1 FROM ONLY WHERE fkatt1 = $1 [AND ...]
+ * SELECT 1 FROM ONLY WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "SELECT 1 FROM ONLY %s x", fkrelname);
+ appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
+ fkrelname);
querysep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), " %s %s = $%d",
- querysep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&querybuf, querysep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, " FOR SHARE OF x");
+ appendStringInfo(&querybuf, " FOR SHARE OF x");
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -1622,7 +1702,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_SELECT,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -1634,7 +1714,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL restrict update.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -1644,7 +1724,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -1659,8 +1739,7 @@ Datum
RI_FKey_setnull_del(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple old_row;
@@ -1673,13 +1752,16 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -1688,11 +1770,11 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
* fk_rel is opened in RowExclusiveLock mode since that's what our
* eventual UPDATE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple;
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -1701,12 +1783,10 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
* ... ON DELETE SET NULL
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_SETNULL_DEL_DOUPDATE,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_SETNULL_DEL_DOUPDATE);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -1736,11 +1816,11 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
*/
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
- char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
const char *qualsep;
Oid queryoids[RI_MAX_NUMKEYS];
@@ -1748,35 +1828,40 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
/* ----------
* The query string built is
* UPDATE ONLY SET fkatt1 = NULL [, ...]
- * WHERE fkatt1 = $1 [AND ...]
+ * WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname);
- qualstr[0] = '\0';
+ appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
querysep = "";
qualsep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = NULL",
- querysep, attname);
- snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d",
- qualsep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ appendStringInfo(&querybuf,
+ "%s %s = NULL",
+ querysep, attname);
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = ",";
qualsep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, qualstr);
+ appendStringInfoString(&querybuf, qualbuf.data);
/* Prepare and save the plan */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, true);
}
@@ -1788,7 +1873,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -1800,7 +1885,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL set null delete.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -1810,7 +1895,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -1825,8 +1910,7 @@ Datum
RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple new_row;
@@ -1834,7 +1918,6 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
RI_QueryKey qkey;
void *qplan;
int i;
- int match_type;
bool use_cached_query;
/*
@@ -1842,13 +1925,16 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -1857,13 +1943,12 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
* fk_rel is opened in RowExclusiveLock mode since that's what our
* eventual UPDATE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
new_row = trigdata->tg_newtuple;
old_row = trigdata->tg_trigtuple;
- match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
- switch (match_type)
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -1872,12 +1957,10 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
* ... ON UPDATE SET NULL
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_SETNULL_UPD_DOUPDATE,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_SETNULL_UPD_DOUPDATE);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -1902,8 +1985,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
/*
* No need to do anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX))
+ if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
{
heap_close(fk_rel, RowExclusiveLock);
return PointerGetDatum(NULL);
@@ -1925,9 +2007,9 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
* know that the old key value has no NULLs (see above).
*/
- use_cached_query = match_type == RI_MATCH_TYPE_FULL ||
+ use_cached_query = (riinfo.confmatchtype == FKCONSTR_MATCH_FULL) ||
ri_AllKeysUnequal(pk_rel, old_row, new_row,
- &qkey, RI_KEYPAIR_PK_IDX);
+ &riinfo, true);
/*
* Fetch or prepare a saved plan for the set null update operation
@@ -1936,11 +2018,11 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
if (!use_cached_query ||
(qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
- char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
const char *qualsep;
Oid queryoids[RI_MAX_NUMKEYS];
@@ -1948,48 +2030,52 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
/* ----------
* The query string built is
* UPDATE ONLY SET fkatt1 = NULL [, ...]
- * WHERE fkatt1 = $1 [AND ...]
+ * WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname);
- qualstr[0] = '\0';
+ appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
querysep = "";
qualsep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
- quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+ quoteOneName(attname,
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
/*
* MATCH - only change columns corresponding
* to changed columns in pk_rel's key
*/
- if (match_type == RI_MATCH_TYPE_FULL ||
- !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX))
+ if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
+ !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
+ &riinfo, true))
{
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = NULL",
- querysep, attname);
+ appendStringInfo(&querybuf,
+ "%s %s = NULL",
+ querysep, attname);
querysep = ",";
}
- snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d",
- qualsep, attname, i + 1);
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
qualsep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, qualstr);
+ appendStringInfoString(&querybuf, qualbuf.data);
/*
* Prepare the plan. Save it only if we're building the
* "standard" plan.
*/
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel,
use_cached_query);
}
@@ -2002,7 +2088,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -2014,7 +2100,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL set null update.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -2024,7 +2110,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -2039,8 +2125,7 @@ Datum
RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple old_row;
@@ -2052,13 +2137,16 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
*/
ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -2067,11 +2155,11 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
* fk_rel is opened in RowExclusiveLock mode since that's what our
* eventual UPDATE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
old_row = trigdata->tg_trigtuple;
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -2080,12 +2168,10 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
* ... ON DELETE SET DEFAULT
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_SETNULL_DEL_DOUPDATE,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_SETNULL_DEL_DOUPDATE);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -2116,11 +2202,11 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
* default value could potentially change between calls.
*/
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
- char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
const char *qualsep;
Oid queryoids[RI_MAX_NUMKEYS];
@@ -2129,35 +2215,40 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
/* ----------
* The query string built is
* UPDATE ONLY SET fkatt1 = DEFAULT [, ...]
- * WHERE fkatt1 = $1 [AND ...]
+ * WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname);
- qualstr[0] = '\0';
+ appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
querysep = "";
qualsep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = DEFAULT",
- querysep, attname);
- snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d",
- qualsep, attname, i + 1);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ appendStringInfo(&querybuf,
+ "%s %s = DEFAULT",
+ querysep, attname);
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
querysep = ",";
qualsep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, qualstr);
+ appendStringInfoString(&querybuf, qualbuf.data);
/* Prepare the plan, don't save it */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, false);
}
@@ -2169,7 +2260,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -2191,7 +2282,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL set null delete.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -2201,7 +2292,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -2216,28 +2307,29 @@ Datum
RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
Relation pk_rel;
HeapTuple new_row;
HeapTuple old_row;
RI_QueryKey qkey;
void *qplan;
- int match_type;
/*
* Check that this is a valid trigger call on the right time and event.
*/
ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
- tgnargs = trigdata->tg_trigger->tgnargs;
- tgargs = trigdata->tg_trigger->tgargs;
+ /*
+ * Get arguments.
+ */
+ ri_FetchConstraintInfo(&riinfo,
+ trigdata->tg_trigger, trigdata->tg_relation, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return PointerGetDatum(NULL);
/*
@@ -2246,14 +2338,12 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
* fk_rel is opened in RowExclusiveLock mode since that's what our
* eventual UPDATE will get on it.
*/
- fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
+ fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
pk_rel = trigdata->tg_relation;
new_row = trigdata->tg_newtuple;
old_row = trigdata->tg_trigtuple;
- match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
-
- switch (match_type)
+ switch (riinfo.confmatchtype)
{
/* ----------
* SQL3 11.9
@@ -2262,12 +2352,10 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
* ... ON UPDATE SET DEFAULT
* ----------
*/
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid,
- RI_PLAN_SETNULL_DEL_DOUPDATE,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_SETNULL_DEL_DOUPDATE);
switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
{
@@ -2292,8 +2380,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
/*
* No need to do anything if old and new keys are equal
*/
- if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX))
+ if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
{
heap_close(fk_rel, RowExclusiveLock);
return PointerGetDatum(NULL);
@@ -2308,11 +2395,11 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
* default value could potentially change between calls.
*/
{
- char querystr[MAX_QUOTED_REL_NAME_LEN + 100 +
- (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2];
- char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS];
+ StringInfoData querybuf;
+ StringInfoData qualbuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
+ char paramname[16];
const char *querysep;
const char *qualsep;
Oid queryoids[RI_MAX_NUMKEYS];
@@ -2321,45 +2408,50 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
/* ----------
* The query string built is
* UPDATE ONLY SET fkatt1 = DEFAULT [, ...]
- * WHERE fkatt1 = $1 [AND ...]
+ * WHERE $1 = fkatt1 [AND ...]
* The type id's for the $ parameters are those of the
- * corresponding PK attributes. Thus, ri_PlanCheck could
- * eventually fail if the parser cannot identify some way
- * how to compare these two types by '='.
+ * corresponding PK attributes.
* ----------
*/
+ initStringInfo(&querybuf);
+ initStringInfo(&qualbuf);
quoteRelationName(fkrelname, fk_rel);
- snprintf(querystr, sizeof(querystr), "UPDATE ONLY %s SET", fkrelname);
- qualstr[0] = '\0';
+ appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
querysep = "";
qualsep = "WHERE";
- for (i = 0; i < qkey.nkeypairs; i++)
+ for (i = 0; i < riinfo.nkeys; i++)
{
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
quoteOneName(attname,
- tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]);
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
/*
* MATCH - only change columns corresponding
* to changed columns in pk_rel's key
*/
- if (match_type == RI_MATCH_TYPE_FULL ||
- !ri_OneKeyEqual(pk_rel, i, old_row,
- new_row, &qkey, RI_KEYPAIR_PK_IDX))
+ if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
+ !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
+ &riinfo, true))
{
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = DEFAULT",
- querysep, attname);
+ appendStringInfo(&querybuf,
+ "%s %s = DEFAULT",
+ querysep, attname);
querysep = ",";
}
- snprintf(qualstr + strlen(qualstr), sizeof(qualstr) - strlen(qualstr), " %s %s = $%d",
- qualsep, attname, i + 1);
+ sprintf(paramname, "$%d", i + 1);
+ ri_GenerateQual(&qualbuf, qualsep,
+ paramname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ attname, fk_type);
qualsep = "AND";
- queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
- qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ queryoids[i] = pk_type;
}
- strcat(querystr, qualstr);
+ appendStringInfoString(&querybuf, qualbuf.data);
/* Prepare the plan, don't save it */
- qplan = ri_PlanCheck(querystr, qkey.nkeypairs, queryoids,
+ qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
&qkey, fk_rel, pk_rel, false);
}
@@ -2371,7 +2463,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
old_row, NULL,
true, /* must detect new rows */
SPI_OK_UPDATE,
- tgargs[RI_CONSTRAINT_NAME_ARGNO]);
+ NameStr(riinfo.conname));
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
@@ -2393,7 +2485,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
/*
* Handle MATCH PARTIAL set null delete.
*/
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -2403,7 +2495,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
/*
* Never reached
*/
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return PointerGetDatum(NULL);
}
@@ -2420,57 +2512,37 @@ bool
RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
HeapTuple old_row, HeapTuple new_row)
{
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation fk_rel;
RI_QueryKey qkey;
/*
- * Check for the correct # of call arguments
+ * Get arguments.
*/
- tgnargs = trigger->tgnargs;
- tgargs = trigger->tgargs;
- if (tgnargs < 4 ||
- tgnargs > RI_MAX_ARGUMENTS ||
- (tgnargs % 2) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
- errmsg("function \"%s\" called with wrong number of trigger arguments",
- "RI_FKey_keyequal_upd")));
+ ri_FetchConstraintInfo(&riinfo, trigger, pk_rel, true);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return true;
- if (!OidIsValid(trigger->tgconstrrelid))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("no target table given for trigger \"%s\" on table \"%s\"",
- trigger->tgname,
- RelationGetRelationName(pk_rel)),
- errhint("Remove this referential integrity trigger and its mates, "
- "then do ALTER TABLE ADD CONSTRAINT.")));
+ fk_rel = heap_open(riinfo.fk_relid, AccessShareLock);
- fk_rel = heap_open(trigger->tgconstrrelid, AccessShareLock);
-
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigger->tgoid,
- RI_PLAN_KEYEQUAL_UPD,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_KEYEQUAL_UPD);
+
heap_close(fk_rel, AccessShareLock);
/* Return if key's are equal */
- return ri_KeysEqual(pk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_PK_IDX);
+ return ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true);
/* Handle MATCH PARTIAL set null delete. */
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -2478,7 +2550,7 @@ RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
}
/* Never reached */
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return false;
}
@@ -2494,57 +2566,36 @@ bool
RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
HeapTuple old_row, HeapTuple new_row)
{
- int tgnargs;
- char **tgargs;
+ RI_ConstraintInfo riinfo;
Relation pk_rel;
RI_QueryKey qkey;
/*
- * Check for the correct # of call arguments
+ * Get arguments.
*/
- tgnargs = trigger->tgnargs;
- tgargs = trigger->tgargs;
- if (tgnargs < 4 ||
- tgnargs > RI_MAX_ARGUMENTS ||
- (tgnargs % 2) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
- errmsg("function \"%s\" called with wrong number of trigger arguments",
- "RI_FKey_keyequal_upd")));
+ ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
/*
* Nothing to do if no column names to compare given
*/
- if (tgnargs == 4)
+ if (riinfo.nkeys == 0)
return true;
- if (!OidIsValid(trigger->tgconstrrelid))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("no target table given for trigger \"%s\" on table \"%s\"",
- trigger->tgname,
- RelationGetRelationName(fk_rel)),
- errhint("Remove this referential integrity trigger and its mates, "
- "then do ALTER TABLE ADD CONSTRAINT.")));
+ pk_rel = heap_open(riinfo.pk_relid, AccessShareLock);
- pk_rel = heap_open(trigger->tgconstrrelid, AccessShareLock);
-
- switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
+ switch (riinfo.confmatchtype)
{
- case RI_MATCH_TYPE_UNSPECIFIED:
- case RI_MATCH_TYPE_FULL:
- ri_BuildQueryKeyFull(&qkey, trigger->tgoid,
- RI_PLAN_KEYEQUAL_UPD,
- fk_rel, pk_rel,
- tgnargs, tgargs);
+ case FKCONSTR_MATCH_UNSPECIFIED:
+ case FKCONSTR_MATCH_FULL:
+ ri_BuildQueryKeyFull(&qkey, &riinfo,
+ RI_PLAN_KEYEQUAL_UPD);
heap_close(pk_rel, AccessShareLock);
/* Return if key's are equal */
- return ri_KeysEqual(fk_rel, old_row, new_row, &qkey,
- RI_KEYPAIR_FK_IDX);
+ return ri_KeysEqual(fk_rel, old_row, new_row, &riinfo, false);
/* Handle MATCH PARTIAL set null delete. */
- case RI_MATCH_TYPE_PARTIAL:
+ case FKCONSTR_MATCH_PARTIAL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("MATCH PARTIAL not yet implemented")));
@@ -2552,7 +2603,7 @@ RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
}
/* Never reached */
- elog(ERROR, "invalid match_type");
+ elog(ERROR, "invalid confmatchtype");
return false;
}
@@ -2572,18 +2623,17 @@ RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
* ----------
*/
bool
-RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
+RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
- const char *constrname = fkconstraint->constr_name;
- char querystr[MAX_QUOTED_REL_NAME_LEN * 2 + 250 +
- (MAX_QUOTED_NAME_LEN + 32) * ((RI_MAX_NUMKEYS * 4) + 1)];
+ RI_ConstraintInfo riinfo;
+ const char *constrname = trigger->tgname;
+ StringInfoData querybuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
- char relname[MAX_QUOTED_REL_NAME_LEN];
- char attname[MAX_QUOTED_NAME_LEN];
- char fkattname[MAX_QUOTED_NAME_LEN];
+ char fkrelname[MAX_QUOTED_REL_NAME_LEN];
+ char pkattname[MAX_QUOTED_NAME_LEN + 3];
+ char fkattname[MAX_QUOTED_NAME_LEN + 3];
const char *sep;
- ListCell *l;
- ListCell *l2;
+ int i;
int old_work_mem;
char workmembuf[32];
int spi_result;
@@ -2596,11 +2646,13 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
*
* XXX are there any other show-stopper conditions to check?
*/
- if (pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
+ if (pg_class_aclcheck(RelationGetRelid(fk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
return false;
- if (pg_class_aclcheck(RelationGetRelid(pkrel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
+ if (pg_class_aclcheck(RelationGetRelid(pk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
return false;
+ ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
+
/*----------
* The query string built is:
* SELECT fk.keycols FROM ONLY relname fk
@@ -2613,50 +2665,57 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
* (fk.keycol1 IS NOT NULL [OR ...])
*----------
*/
-
- sprintf(querystr, "SELECT ");
+ initStringInfo(&querybuf);
+ appendStringInfo(&querybuf, "SELECT ");
sep = "";
- foreach(l, fkconstraint->fk_attrs)
+ for (i = 0; i < riinfo.nkeys; i++)
{
- quoteOneName(attname, strVal(lfirst(l)));
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr),
- "%sfk.%s", sep, attname);
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
- quoteRelationName(pkrelname, pkrel);
- quoteRelationName(relname, rel);
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr),
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON (",
- relname, pkrelname);
+ quoteRelationName(pkrelname, pk_rel);
+ quoteRelationName(fkrelname, fk_rel);
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
- sep = "";
- forboth(l, fkconstraint->pk_attrs, l2, fkconstraint->fk_attrs)
+ strcpy(pkattname, "pk.");
+ strcpy(fkattname, "fk.");
+ sep = "(";
+ for (i = 0; i < riinfo.nkeys; i++)
{
- quoteOneName(attname, strVal(lfirst(l)));
- quoteOneName(fkattname, strVal(lfirst(l2)));
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr),
- "%spk.%s=fk.%s",
- sep, attname, fkattname);
- sep = " AND ";
+ Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
+ Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
+
+ quoteOneName(pkattname + 3,
+ RIAttName(pk_rel, riinfo.pk_attnums[i]));
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ ri_GenerateQual(&querybuf, sep,
+ pkattname, pk_type,
+ riinfo.pf_eq_oprs[i],
+ fkattname, fk_type);
+ sep = "AND";
}
/*
* It's sufficient to test any one pk attribute for null to detect a join
* failure.
*/
- quoteOneName(attname, strVal(linitial(fkconstraint->pk_attrs)));
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr),
- ") WHERE pk.%s IS NULL AND (", attname);
+ quoteOneName(pkattname, RIAttName(pk_rel, riinfo.pk_attnums[0]));
+ appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
sep = "";
- foreach(l, fkconstraint->fk_attrs)
+ for (i = 0; i < riinfo.nkeys; i++)
{
- quoteOneName(attname, strVal(lfirst(l)));
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr),
- "%sfk.%s IS NOT NULL",
- sep, attname);
- switch (fkconstraint->fk_matchtype)
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo.fk_attnums[i]));
+ appendStringInfo(&querybuf,
+ "%sfk.%s IS NOT NULL",
+ sep, fkattname);
+ switch (riinfo.confmatchtype)
{
case FKCONSTR_MATCH_UNSPECIFIED:
sep = " AND ";
@@ -2671,12 +2730,11 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
break;
default:
elog(ERROR, "unrecognized match type: %d",
- fkconstraint->fk_matchtype);
+ riinfo.confmatchtype);
break;
}
}
- snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr),
- ")");
+ appendStringInfo(&querybuf, ")");
/*
* Temporarily increase work_mem so that the check query can be executed
@@ -2702,10 +2760,11 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
* Generate the plan. We don't need to cache it, and there are no
* arguments to the plan.
*/
- qplan = SPI_prepare(querystr, 0, NULL);
+ qplan = SPI_prepare(querybuf.data, 0, NULL);
if (qplan == NULL)
- elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
+ elog(ERROR, "SPI_prepare returned %d for %s",
+ SPI_result, querybuf.data);
/*
* Run the plan. For safety we force a current snapshot to be used. (In
@@ -2728,8 +2787,6 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
{
HeapTuple tuple = SPI_tuptable->vals[0];
TupleDesc tupdesc = SPI_tuptable->tupdesc;
- int nkeys = list_length(fkconstraint->fk_attrs);
- int i;
RI_QueryKey qkey;
/*
@@ -2737,11 +2794,11 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
* complain about that rather than the lack of a match. MATCH FULL
* disallows partially-null FK rows.
*/
- if (fkconstraint->fk_matchtype == FKCONSTR_MATCH_FULL)
+ if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL)
{
bool isnull = false;
- for (i = 1; i <= nkeys; i++)
+ for (i = 1; i <= riinfo.nkeys; i++)
{
(void) SPI_getbinval(tuple, tupdesc, i, &isnull);
if (isnull)
@@ -2751,7 +2808,7 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
ereport(ERROR,
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
- RelationGetRelationName(rel),
+ RelationGetRelationName(fk_rel),
constrname),
errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
}
@@ -2762,12 +2819,12 @@ RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel)
*/
MemSet(&qkey, 0, sizeof(qkey));
qkey.constr_queryno = RI_PLAN_CHECK_LOOKUPPK;
- qkey.nkeypairs = nkeys;
- for (i = 0; i < nkeys; i++)
+ qkey.nkeypairs = riinfo.nkeys;
+ for (i = 0; i < riinfo.nkeys; i++)
qkey.keypair[i][RI_KEYPAIR_FK_IDX] = i + 1;
ri_ReportViolation(&qkey, constrname,
- pkrel, rel,
+ pk_rel, fk_rel,
tuple, tupdesc,
false);
}
@@ -2829,90 +2886,80 @@ quoteRelationName(char *buffer, Relation rel)
quoteOneName(buffer, RelationGetRelationName(rel));
}
-
-/* ----------
- * ri_DetermineMatchType -
+/*
+ * ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * Convert the MATCH TYPE string into a switchable int
- * ----------
+ * The idea is to append " sep leftop op rightop" to buf. The complexity
+ * comes from needing to be sure that the parser will select the desired
+ * operator. We always name the operator using OPERATOR(schema.op) syntax
+ * (readability isn't a big priority here). We have to emit casts too,
+ * if either input isn't already the input type of the operator.
*/
-static int
-ri_DetermineMatchType(char *str)
+static void
+ri_GenerateQual(StringInfo buf,
+ const char *sep,
+ const char *leftop, Oid leftoptype,
+ Oid opoid,
+ const char *rightop, Oid rightoptype)
{
- if (strcmp(str, "UNSPECIFIED") == 0)
- return RI_MATCH_TYPE_UNSPECIFIED;
- if (strcmp(str, "FULL") == 0)
- return RI_MATCH_TYPE_FULL;
- if (strcmp(str, "PARTIAL") == 0)
- return RI_MATCH_TYPE_PARTIAL;
+ HeapTuple opertup;
+ Form_pg_operator operform;
+ char *oprname;
+ char *nspname;
- elog(ERROR, "unrecognized referential integrity match type \"%s\"", str);
- return 0;
+ opertup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(opoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(opertup))
+ elog(ERROR, "cache lookup failed for operator %u", opoid);
+ operform = (Form_pg_operator) GETSTRUCT(opertup);
+ Assert(operform->oprkind == 'b');
+ oprname = NameStr(operform->oprname);
+
+ nspname = get_namespace_name(operform->oprnamespace);
+
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ appendStringInfo(buf, "::%s", format_type_be(operform->oprleft));
+ appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
+ appendStringInfoString(buf, oprname);
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ appendStringInfo(buf, "::%s", format_type_be(operform->oprright));
+
+ ReleaseSysCache(opertup);
}
-
/* ----------
* ri_BuildQueryKeyFull -
*
* Build up a new hashtable key for a prepared SPI plan of a
- * constraint trigger of MATCH FULL. The key consists of:
+ * constraint trigger of MATCH FULL.
*
- * constr_type is FULL
- * constr_id is the OID of the pg_trigger row that invoked us
- * constr_queryno is an internal number of the query inside the proc
- * fk_relid is the OID of referencing relation
- * pk_relid is the OID of referenced relation
- * nkeypairs is the number of keypairs
- * following are the attribute number keypairs of the trigger invocation
+ * key: output argument, *key is filled in based on the other arguments
+ * riinfo: info from pg_constraint entry
+ * constr_queryno: an internal number of the query inside the proc
*
* At least for MATCH FULL this builds a unique key per plan.
* ----------
*/
static void
-ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
- Relation fk_rel, Relation pk_rel,
- int argc, char **argv)
+ri_BuildQueryKeyFull(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
+ int32 constr_queryno)
{
int i;
- int j;
- int fno;
- /*
- * Initialize the key and fill in type, oid's and number of keypairs
- */
- memset(key, 0, sizeof(RI_QueryKey));
- key->constr_type = RI_MATCH_TYPE_FULL;
- key->constr_id = constr_id;
+ MemSet(key, 0, sizeof(RI_QueryKey));
+ key->constr_type = FKCONSTR_MATCH_FULL;
+ key->constr_id = riinfo->constraint_id;
key->constr_queryno = constr_queryno;
- key->fk_relid = fk_rel->rd_id;
- key->pk_relid = pk_rel->rd_id;
- key->nkeypairs = (argc - RI_FIRST_ATTNAME_ARGNO) / 2;
-
- /*
- * Lookup the attribute numbers of the arguments to the trigger call and
- * fill in the keypairs.
- */
- for (i = 0, j = RI_FIRST_ATTNAME_ARGNO; j < argc; i++, j += 2)
+ key->fk_relid = riinfo->fk_relid;
+ key->pk_relid = riinfo->pk_relid;
+ key->nkeypairs = riinfo->nkeys;
+ for (i = 0; i < riinfo->nkeys; i++)
{
- fno = SPI_fnumber(fk_rel->rd_att, argv[j]);
- if (fno == SPI_ERROR_NOATTRIBUTE)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("table \"%s\" does not have column \"%s\" referenced by constraint \"%s\"",
- RelationGetRelationName(fk_rel),
- argv[j],
- argv[RI_CONSTRAINT_NAME_ARGNO])));
- key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;
-
- fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
- if (fno == SPI_ERROR_NOATTRIBUTE)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("table \"%s\" does not have column \"%s\" referenced by constraint \"%s\"",
- RelationGetRelationName(pk_rel),
- argv[j + 1],
- argv[RI_CONSTRAINT_NAME_ARGNO])));
- key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
+ key->keypair[i][RI_KEYPAIR_FK_IDX] = riinfo->fk_attnums[i];
+ key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
}
}
@@ -2923,7 +2970,6 @@ static void
ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
- int tgnargs;
if (!CALLED_AS_TRIGGER(fcinfo))
ereport(ERROR,
@@ -2968,30 +3014,148 @@ ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
errmsg("function \"%s\" must be fired for DELETE", funcname)));
break;
}
+}
+
+
+/*
+ * Fetch the pg_constraint entry for the FK constraint, and fill *riinfo
+ */
+static void
+ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
+ Trigger *trigger, Relation trig_rel, bool rel_is_pk)
+{
+ Oid constraintOid = trigger->tgconstraint;
+ HeapTuple tup;
+ Form_pg_constraint conForm;
+ Datum adatum;
+ bool isNull;
+ ArrayType *arr;
+ int numkeys;
/*
- * Check for the correct # of call arguments
+ * Check that the FK constraint's OID is available; it might not be
+ * if we've been invoked via an ordinary trigger or an old-style
+ * "constraint trigger".
*/
- tgnargs = trigdata->tg_trigger->tgnargs;
- if (tgnargs < 4 ||
- tgnargs > RI_MAX_ARGUMENTS ||
- (tgnargs % 2) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
- errmsg("function \"%s\" called with wrong number of trigger arguments",
- funcname)));
-
- /*
- * Check that tgconstrrelid is known. We need to check here because of
- * ancient pg_dump bug; see notes in CreateTrigger().
- */
- if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
+ if (!OidIsValid(constraintOid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("no target table given for trigger \"%s\" on table \"%s\"",
- trigdata->tg_trigger->tgname,
- RelationGetRelationName(trigdata->tg_relation)),
+ errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
+ trigger->tgname, RelationGetRelationName(trig_rel)),
errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
+
+ /* OK, fetch the tuple */
+ tup = SearchSysCache(CONSTROID,
+ ObjectIdGetDatum(constraintOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
+ conForm = (Form_pg_constraint) GETSTRUCT(tup);
+
+ /* Do some easy cross-checks against the trigger call data */
+ if (rel_is_pk)
+ {
+ if (conForm->contype != CONSTRAINT_FOREIGN ||
+ conForm->conrelid != trigger->tgconstrrelid ||
+ conForm->confrelid != RelationGetRelid(trig_rel))
+ elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
+ trigger->tgname, RelationGetRelationName(trig_rel));
+ }
+ else
+ {
+ if (conForm->contype != CONSTRAINT_FOREIGN ||
+ conForm->conrelid != RelationGetRelid(trig_rel) ||
+ conForm->confrelid != trigger->tgconstrrelid)
+ elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
+ trigger->tgname, RelationGetRelationName(trig_rel));
+ }
+
+ /* And extract data */
+ riinfo->constraint_id = constraintOid;
+ memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
+ riinfo->pk_relid = conForm->confrelid;
+ riinfo->fk_relid = conForm->conrelid;
+ riinfo->confupdtype = conForm->confupdtype;
+ riinfo->confdeltype = conForm->confdeltype;
+ riinfo->confmatchtype = conForm->confmatchtype;
+
+ /*
+ * We expect the arrays to be 1-D arrays of the right types; verify that.
+ * We don't need to use deconstruct_array() since the array data is
+ * just going to look like a C array of values.
+ */
+ adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey, &isNull);
+ if (isNull)
+ elog(ERROR, "null conkey for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys < 0 ||
+ numkeys > RI_MAX_NUMKEYS ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != INT2OID)
+ elog(ERROR, "conkey is not a 1-D smallint array");
+ riinfo->nkeys = numkeys;
+ memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey, &isNull);
+ if (isNull)
+ elog(ERROR, "null confkey for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys != riinfo->nkeys ||
+ numkeys > RI_MAX_NUMKEYS ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != INT2OID)
+ elog(ERROR, "confkey is not a 1-D smallint array");
+ memcpy(riinfo->pk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conpfeqop, &isNull);
+ if (isNull)
+ elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys != riinfo->nkeys ||
+ numkeys > RI_MAX_NUMKEYS ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "conpfeqop is not a 1-D Oid array");
+ memcpy(riinfo->pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conppeqop, &isNull);
+ if (isNull)
+ elog(ERROR, "null conppeqop for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys != riinfo->nkeys ||
+ numkeys > RI_MAX_NUMKEYS ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "conppeqop is not a 1-D Oid array");
+ memcpy(riinfo->pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conffeqop, &isNull);
+ if (isNull)
+ elog(ERROR, "null conffeqop for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys != riinfo->nkeys ||
+ numkeys > RI_MAX_NUMKEYS ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != OIDOID)
+ elog(ERROR, "conffeqop is not a 1-D Oid array");
+ memcpy(riinfo->ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
+
+ ReleaseSysCache(tup);
}
@@ -3313,52 +3477,30 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
* Build up a new hashtable key for a prepared SPI plan of a
* check for PK rows in noaction triggers.
*
- * constr_type is FULL
- * constr_id is the OID of the pg_trigger row that invoked us
- * constr_queryno is an internal number of the query inside the proc
- * pk_relid is the OID of referenced relation
- * nkeypairs is the number of keypairs
- * following are the attribute number keypairs of the trigger invocation
+ * key: output argument, *key is filled in based on the other arguments
+ * riinfo: info from pg_constraint entry
+ * constr_queryno: an internal number of the query inside the proc
*
* At least for MATCH FULL this builds a unique key per plan.
* ----------
*/
static void
-ri_BuildQueryKeyPkCheck(RI_QueryKey *key, Oid constr_id, int32 constr_queryno,
- Relation pk_rel,
- int argc, char **argv)
+ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
+ int32 constr_queryno)
{
int i;
- int j;
- int fno;
- /*
- * Initialize the key and fill in type, oid's and number of keypairs
- */
- memset((void *) key, 0, sizeof(RI_QueryKey));
- key->constr_type = RI_MATCH_TYPE_FULL;
- key->constr_id = constr_id;
+ MemSet(key, 0, sizeof(RI_QueryKey));
+ key->constr_type = FKCONSTR_MATCH_FULL;
+ key->constr_id = riinfo->constraint_id;
key->constr_queryno = constr_queryno;
- key->fk_relid = 0;
- key->pk_relid = pk_rel->rd_id;
- key->nkeypairs = (argc - RI_FIRST_ATTNAME_ARGNO) / 2;
-
- /*
- * Lookup the attribute numbers of the arguments to the trigger call and
- * fill in the keypairs.
- */
- for (i = 0, j = RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX; j < argc; i++, j += 2)
+ key->fk_relid = InvalidOid;
+ key->pk_relid = riinfo->pk_relid;
+ key->nkeypairs = riinfo->nkeys;
+ for (i = 0; i < riinfo->nkeys; i++)
{
- fno = SPI_fnumber(pk_rel->rd_att, argv[j]);
- if (fno == SPI_ERROR_NOATTRIBUTE)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("table \"%s\" does not have column \"%s\" referenced by constraint \"%s\"",
- RelationGetRelationName(pk_rel),
- argv[j],
- argv[RI_CONSTRAINT_NAME_ARGNO])));
- key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
key->keypair[i][RI_KEYPAIR_FK_IDX] = 0;
+ key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
}
}
@@ -3402,8 +3544,8 @@ ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
/* ----------
* ri_InitHashTables -
*
- * Initialize our internal hash table for prepared
- * query plans.
+ * Initialize our internal hash tables for prepared
+ * query plans and comparison operators.
* ----------
*/
static void
@@ -3417,6 +3559,13 @@ ri_InitHashTables(void)
ctl.hash = tag_hash;
ri_query_cache = hash_create("RI query cache", RI_INIT_QUERYHASHSIZE,
&ctl, HASH_ELEM | HASH_FUNCTION);
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(RI_CompareKey);
+ ctl.entrysize = sizeof(RI_CompareHashEntry);
+ ctl.hash = tag_hash;
+ ri_compare_cache = hash_create("RI compare cache", RI_INIT_QUERYHASHSIZE,
+ &ctl, HASH_ELEM | HASH_FUNCTION);
}
@@ -3486,38 +3635,49 @@ ri_HashPreparedPlan(RI_QueryKey *key, void *plan)
*/
static bool
ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
- RI_QueryKey *key, int pairidx)
+ const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
+ TupleDesc tupdesc = RelationGetDescr(rel);
+ const int16 *attnums;
+ const Oid *eq_oprs;
int i;
- Oid typeid;
- Datum oldvalue;
- Datum newvalue;
- bool isnull;
- for (i = 0; i < key->nkeypairs; i++)
+ if (rel_is_pk)
{
- /*
- * Get one attribute's oldvalue. If it is NULL - they're not equal.
- */
- oldvalue = SPI_getbinval(oldtup, rel->rd_att,
- key->keypair[i][pairidx], &isnull);
- if (isnull)
- return false;
+ attnums = riinfo->pk_attnums;
+ eq_oprs = riinfo->pp_eq_oprs;
+ }
+ else
+ {
+ attnums = riinfo->fk_attnums;
+ eq_oprs = riinfo->ff_eq_oprs;
+ }
+
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ Datum oldvalue;
+ Datum newvalue;
+ bool isnull;
/*
* Get one attribute's oldvalue. If it is NULL - they're not equal.
*/
- newvalue = SPI_getbinval(newtup, rel->rd_att,
- key->keypair[i][pairidx], &isnull);
+ oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
if (isnull)
return false;
/*
- * Get the attribute's type OID and call the '=' operator to compare
- * the values.
+ * Get one attribute's newvalue. If it is NULL - they're not equal.
*/
- typeid = SPI_gettypeid(rel->rd_att, key->keypair[i][pairidx]);
- if (!ri_AttributesEqual(typeid, oldvalue, newvalue))
+ newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
+ if (isnull)
+ return false;
+
+ /*
+ * Compare them with the appropriate equality operator.
+ */
+ if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
+ oldvalue, newvalue))
return false;
}
@@ -3533,52 +3693,61 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
*/
static bool
ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
- RI_QueryKey *key, int pairidx)
+ const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
+ TupleDesc tupdesc = RelationGetDescr(rel);
+ const int16 *attnums;
+ const Oid *eq_oprs;
int i;
- Oid typeid;
- Datum oldvalue;
- Datum newvalue;
- bool isnull;
- bool keys_unequal;
- keys_unequal = true;
- for (i = 0; keys_unequal && i < key->nkeypairs; i++)
+ if (rel_is_pk)
{
- /*
- * Get one attributes oldvalue. If it is NULL - they're not equal.
- */
- oldvalue = SPI_getbinval(oldtup, rel->rd_att,
- key->keypair[i][pairidx], &isnull);
- if (isnull)
- continue;
-
- /*
- * Get one attributes oldvalue. If it is NULL - they're not equal.
- */
- newvalue = SPI_getbinval(newtup, rel->rd_att,
- key->keypair[i][pairidx], &isnull);
- if (isnull)
- continue;
-
- /*
- * Get the attributes type OID and call the '=' operator to compare
- * the values.
- */
- typeid = SPI_gettypeid(rel->rd_att, key->keypair[i][pairidx]);
- if (!ri_AttributesEqual(typeid, oldvalue, newvalue))
- continue;
- keys_unequal = false;
+ attnums = riinfo->pk_attnums;
+ eq_oprs = riinfo->pp_eq_oprs;
+ }
+ else
+ {
+ attnums = riinfo->fk_attnums;
+ eq_oprs = riinfo->ff_eq_oprs;
}
- return keys_unequal;
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ Datum oldvalue;
+ Datum newvalue;
+ bool isnull;
+
+ /*
+ * Get one attribute's oldvalue. If it is NULL - they're not equal.
+ */
+ oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
+ if (isnull)
+ continue;
+
+ /*
+ * Get one attribute's newvalue. If it is NULL - they're not equal.
+ */
+ newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
+ if (isnull)
+ continue;
+
+ /*
+ * Compare them with the appropriate equality operator.
+ */
+ if (ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
+ oldvalue, newvalue))
+ return false; /* found two equal items */
+ }
+
+ return true;
}
/* ----------
* ri_OneKeyEqual -
*
- * Check if one key value in OLD and NEW is equal.
+ * Check if one key value in OLD and NEW is equal. Note column is indexed
+ * from zero.
*
* ri_KeysEqual could call this but would run a bit slower. For
* now, let's duplicate the code.
@@ -3586,73 +3755,158 @@ ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
*/
static bool
ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
- RI_QueryKey *key, int pairidx)
+ const RI_ConstraintInfo *riinfo, bool rel_is_pk)
{
- Oid typeid;
+ TupleDesc tupdesc = RelationGetDescr(rel);
+ const int16 *attnums;
+ const Oid *eq_oprs;
Datum oldvalue;
Datum newvalue;
bool isnull;
+ if (rel_is_pk)
+ {
+ attnums = riinfo->pk_attnums;
+ eq_oprs = riinfo->pp_eq_oprs;
+ }
+ else
+ {
+ attnums = riinfo->fk_attnums;
+ eq_oprs = riinfo->ff_eq_oprs;
+ }
+
/*
- * Get one attributes oldvalue. If it is NULL - they're not equal.
+ * Get one attribute's oldvalue. If it is NULL - they're not equal.
*/
- oldvalue = SPI_getbinval(oldtup, rel->rd_att,
- key->keypair[column][pairidx], &isnull);
+ oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[column], &isnull);
if (isnull)
return false;
/*
- * Get one attributes oldvalue. If it is NULL - they're not equal.
+ * Get one attribute's newvalue. If it is NULL - they're not equal.
*/
- newvalue = SPI_getbinval(newtup, rel->rd_att,
- key->keypair[column][pairidx], &isnull);
+ newvalue = SPI_getbinval(newtup, tupdesc, attnums[column], &isnull);
if (isnull)
return false;
/*
- * Get the attributes type OID and call the '=' operator to compare the
- * values.
+ * Compare them with the appropriate equality operator.
*/
- typeid = SPI_gettypeid(rel->rd_att, key->keypair[column][pairidx]);
- if (!ri_AttributesEqual(typeid, oldvalue, newvalue))
+ if (!ri_AttributesEqual(eq_oprs[column], RIAttType(rel, attnums[column]),
+ oldvalue, newvalue))
return false;
return true;
}
-
/* ----------
* ri_AttributesEqual -
*
- * Call the type specific '=' operator comparison function
- * for two values.
+ * Call the appropriate equality comparison operator for two values.
*
* NB: we have already checked that neither value is null.
* ----------
*/
static bool
-ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
+ri_AttributesEqual(Oid eq_opr, Oid typeid,
+ Datum oldvalue, Datum newvalue)
{
- TypeCacheEntry *typentry;
+ RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
- /*
- * Find the data type in the typcache, and ask for eq_opr info.
- */
- typentry = lookup_type_cache(typeid, TYPECACHE_EQ_OPR_FINFO);
+ /* Do we need to cast the values? */
+ if (OidIsValid(entry->cast_func_finfo.fn_oid))
+ {
+ oldvalue = FunctionCall3(&entry->cast_func_finfo,
+ oldvalue,
+ Int32GetDatum(-1), /* typmod */
+ BoolGetDatum(false)); /* implicit coercion */
+ newvalue = FunctionCall3(&entry->cast_func_finfo,
+ newvalue,
+ Int32GetDatum(-1), /* typmod */
+ BoolGetDatum(false)); /* implicit coercion */
+ }
- if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify an equality operator for type %s",
- format_type_be(typeid))));
-
- /*
- * Call the type specific '=' function
- */
- return DatumGetBool(FunctionCall2(&(typentry->eq_opr_finfo),
+ /* Apply the comparison operator */
+ return DatumGetBool(FunctionCall2(&entry->eq_opr_finfo,
oldvalue, newvalue));
}
+/* ----------
+ * ri_HashCompareOp -
+ *
+ * See if we know how to compare two values, and create a new hash entry
+ * if not.
+ * ----------
+ */
+static RI_CompareHashEntry *
+ri_HashCompareOp(Oid eq_opr, Oid typeid)
+{
+ RI_CompareKey key;
+ RI_CompareHashEntry *entry;
+ bool found;
+
+ /*
+ * On the first call initialize the hashtable
+ */
+ if (!ri_compare_cache)
+ ri_InitHashTables();
+
+ /*
+ * Find or create a hash entry. Note we're assuming RI_CompareKey
+ * contains no struct padding.
+ */
+ key.eq_opr = eq_opr;
+ key.typeid = typeid;
+ entry = (RI_CompareHashEntry *) hash_search(ri_compare_cache,
+ (void *) &key,
+ HASH_ENTER, &found);
+ if (!found)
+ entry->valid = false;
+
+ /*
+ * If not already initialized, do so. Since we'll keep this hash entry
+ * for the life of the backend, put any subsidiary info for the function
+ * cache structs into TopMemoryContext.
+ */
+ if (!entry->valid)
+ {
+ Oid lefttype,
+ righttype,
+ castfunc;
+
+ /* We always need to know how to call the equality operator */
+ fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
+ TopMemoryContext);
+
+ /*
+ * If we chose to use a cast from FK to PK type, we may have to
+ * apply the cast function to get to the operator's input type.
+ */
+ op_input_types(eq_opr, &lefttype, &righttype);
+ Assert(lefttype == righttype);
+ if (typeid == lefttype)
+ castfunc = InvalidOid; /* simplest case */
+ else if (!find_coercion_pathway(lefttype, typeid, COERCION_IMPLICIT,
+ &castfunc))
+ {
+ /* If target is ANYARRAY, assume it's OK, else punt. */
+ if (lefttype != ANYARRAYOID)
+ elog(ERROR, "no conversion function from %s to %s",
+ format_type_be(typeid),
+ format_type_be(lefttype));
+ }
+ if (OidIsValid(castfunc))
+ fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
+ TopMemoryContext);
+ else
+ entry->cast_func_finfo.fn_oid = InvalidOid;
+ entry->valid = true;
+ }
+
+ return entry;
+}
+
+
/*
* Given a trigger function OID, determine whether it is an RI trigger,
* and if so whether it is attached to PK or FK relation.
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3cd317361f..064b8e07f7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.248 2007/02/03 14:06:54 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.249 2007/02/14 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -875,30 +875,15 @@ static char *
pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags)
{
- StringInfoData buf;
- Relation conDesc;
- SysScanDesc conscan;
- ScanKeyData skey[1];
HeapTuple tup;
Form_pg_constraint conForm;
+ StringInfoData buf;
- /*
- * Fetch the pg_constraint row. There's no syscache for pg_constraint so
- * we must do it the hard way.
- */
- conDesc = heap_open(ConstraintRelationId, AccessShareLock);
-
- ScanKeyInit(&skey[0],
- ObjectIdAttributeNumber,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(constraintId));
-
- conscan = systable_beginscan(conDesc, ConstraintOidIndexId, true,
- SnapshotNow, 1, skey);
-
- tup = systable_getnext(conscan);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "could not find tuple for constraint %u", constraintId);
+ tup = SearchSysCache(CONSTROID,
+ ObjectIdGetDatum(constraintId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for constraint %u", constraintId);
conForm = (Form_pg_constraint) GETSTRUCT(tup);
initStringInfo(&buf);
@@ -922,8 +907,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfo(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = heap_getattr(tup, Anum_pg_constraint_conkey,
- RelationGetDescr(conDesc), &isnull);
+ val = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey, &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
@@ -935,8 +920,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
generate_relation_name(conForm->confrelid));
/* Fetch and build referenced-column list */
- val = heap_getattr(tup, Anum_pg_constraint_confkey,
- RelationGetDescr(conDesc), &isnull);
+ val = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey, &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
@@ -1038,8 +1023,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfo(&buf, "UNIQUE (");
/* Fetch and build target column list */
- val = heap_getattr(tup, Anum_pg_constraint_conkey,
- RelationGetDescr(conDesc), &isnull);
+ val = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey, &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
@@ -1071,8 +1056,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
List *context;
/* Fetch constraint expression in parsetree form */
- val = heap_getattr(tup, Anum_pg_constraint_conbin,
- RelationGetDescr(conDesc), &isnull);
+ val = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conbin, &isnull);
if (isnull)
elog(ERROR, "null conbin for constraint %u",
constraintId);
@@ -1115,8 +1100,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
}
/* Cleanup */
- systable_endscan(conscan);
- heap_close(conDesc, AccessShareLock);
+ ReleaseSysCache(tup);
return buf.data;
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 8946cb7315..d6ff883c92 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.147 2007/01/30 01:33:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.148 2007/02/14 01:58:57 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -20,6 +20,7 @@
#include "bootstrap/bootstrap.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
@@ -897,10 +898,37 @@ get_atttypetypmod(Oid relid, AttrNumber attnum,
ReleaseSysCache(tp);
}
-/* ---------- INDEX CACHE ---------- */
+/* ---------- CONSTRAINT CACHE ---------- */
-/* watch this space...
+/*
+ * get_constraint_name
+ * Returns the name of a given pg_constraint entry.
+ *
+ * Returns a palloc'd copy of the string, or NULL if no such constraint.
+ *
+ * NOTE: since constraint name is not unique, be wary of code that uses this
+ * for anything except preparing error messages.
*/
+char *
+get_constraint_name(Oid conoid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache(CONSTROID,
+ ObjectIdGetDatum(conoid),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
+ char *result;
+
+ result = pstrdup(NameStr(contup->conname));
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return NULL;
+}
/* ---------- OPCLASS CACHE ---------- */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 5b33ff423e..75f290dda6 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.110 2007/01/05 22:19:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.111 2007/02/14 01:58:57 tgl Exp $
*
* NOTES
* These routines allow the parser/planner/executor to perform
@@ -28,6 +28,7 @@
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_language.h"
@@ -298,7 +299,19 @@ static const struct cachedesc cacheinfo[] = {
},
128
},
- {ConversionRelationId, /* CONOID */
+ {ConstraintRelationId, /* CONSTROID */
+ ConstraintOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 1024
+ },
+ {ConversionRelationId, /* CONVOID */
ConversionOidIndexId,
0,
1,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2fec7326d5..eef9a4c875 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.459 2007/01/25 03:30:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.460 2007/02/14 01:58:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -3841,11 +3841,30 @@ getTriggers(TableInfo tblinfo[], int numTables)
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
resetPQExpBuffer(query);
- if (g_fout->remoteVersion >= 70300)
+ if (g_fout->remoteVersion >= 80300)
{
/*
* We ignore triggers that are tied to a foreign-key constraint
*/
+ appendPQExpBuffer(query,
+ "SELECT tgname, "
+ "tgfoid::pg_catalog.regproc as tgfname, "
+ "tgtype, tgnargs, tgargs, tgenabled, "
+ "tgisconstraint, tgconstrname, tgdeferrable, "
+ "tgconstrrelid, tginitdeferred, tableoid, oid, "
+ "tgconstrrelid::pg_catalog.regclass as tgconstrrelname "
+ "from pg_catalog.pg_trigger t "
+ "where tgrelid = '%u'::pg_catalog.oid "
+ "and tgconstraint = 0",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (g_fout->remoteVersion >= 70300)
+ {
+ /*
+ * We ignore triggers that are tied to a foreign-key constraint,
+ * but in these versions we have to grovel through pg_constraint
+ * to find out
+ */
appendPQExpBuffer(query,
"SELECT tgname, "
"tgfoid::pg_catalog.regproc as tgfname, "
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 6469b6aee4..3e830d9eb9 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2007, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.150 2007/01/20 21:17:30 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.151 2007/02/14 01:58:58 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
@@ -1128,12 +1128,8 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' "
"AND t.tgenabled "
- "AND (NOT t.tgisconstraint "
- " OR NOT EXISTS"
- " (SELECT 1 FROM pg_catalog.pg_depend d "
- " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
- " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))"
- " ORDER BY 1",
+ "AND t.tgconstraint = 0\n"
+ "ORDER BY 1",
oid);
result4 = PSQLexec(buf.data, false);
if (!result4)
@@ -1152,12 +1148,8 @@ describeOneTableDetails(const char *schemaname,
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' "
"AND NOT t.tgenabled "
- "AND (NOT t.tgisconstraint "
- " OR NOT EXISTS"
- " (SELECT 1 FROM pg_catalog.pg_depend d "
- " JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
- " WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))"
- " ORDER BY 1",
+ "AND t.tgconstraint = 0\n"
+ "ORDER BY 1",
oid);
result7 = PSQLexec(buf.data, false);
if (!result7)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 5bdda05960..aec8dee45c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.383 2007/02/09 03:35:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.384 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200702081
+#define CATALOG_VERSION_NO 200702131
#endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 73e2cf0cc7..baa650a8ed 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.97 2007/01/05 22:19:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.98 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -214,9 +214,6 @@ DECLARE_UNIQUE_INDEX(pg_tablespace_spcname_index, 2698, on pg_tablespace using b
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_trigger_tgconstrname_index, 2699, on pg_trigger using btree(tgconstrname name_ops));
#define TriggerConstrNameIndexId 2699
-/* This following index is not used for a cache and is not unique */
-DECLARE_INDEX(pg_trigger_tgconstrrelid_index, 2700, on pg_trigger using btree(tgconstrrelid oid_ops));
-#define TriggerConstrRelidIndexId 2700
DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using btree(tgrelid oid_ops, tgname name_ops));
#define TriggerRelidNameIndexId 2701
DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 6fbfb6a0cd..d01c22c703 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.24 2007/01/05 22:19:52 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.25 2007/02/14 01:58:58 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -91,6 +91,24 @@ CATALOG(pg_constraint,2606)
*/
int2 confkey[1];
+ /*
+ * If a foreign key, the OIDs of the PK = FK equality operators for each
+ * column of the constraint
+ */
+ Oid conpfeqop[1];
+
+ /*
+ * If a foreign key, the OIDs of the PK = PK equality operators for each
+ * column of the constraint (i.e., equality for the referenced columns)
+ */
+ Oid conppeqop[1];
+
+ /*
+ * If a foreign key, the OIDs of the FK = FK equality operators for each
+ * column of the constraint (i.e., equality for the referencing columns)
+ */
+ Oid conffeqop[1];
+
/*
* If a check constraint, nodeToString representation of expression
*/
@@ -113,7 +131,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 15
+#define Natts_pg_constraint 18
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -127,8 +145,11 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_confmatchtype 11
#define Anum_pg_constraint_conkey 12
#define Anum_pg_constraint_confkey 13
-#define Anum_pg_constraint_conbin 14
-#define Anum_pg_constraint_consrc 15
+#define Anum_pg_constraint_conpfeqop 14
+#define Anum_pg_constraint_conppeqop 15
+#define Anum_pg_constraint_conffeqop 16
+#define Anum_pg_constraint_conbin 17
+#define Anum_pg_constraint_consrc 18
/* Valid values for contype */
@@ -167,6 +188,9 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid domainId,
Oid foreignRelId,
const int16 *foreignKey,
+ const Oid *pfEqOp,
+ const Oid *ppEqOp,
+ const Oid *ffEqOp,
int foreignNKeys,
char foreignUpdateType,
char foreignDeleteType,
@@ -184,8 +208,6 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
const char *label, Oid namespace,
List *others);
-extern char *GetConstraintNameForTrigger(Oid triggerId);
-
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType);
diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h
index b5408253b3..df22089e31 100644
--- a/src/include/catalog/pg_trigger.h
+++ b/src/include/catalog/pg_trigger.h
@@ -1,11 +1,14 @@
/*-------------------------------------------------------------------------
*
* pg_trigger.h
+ * definition of the system "trigger" relation (pg_trigger)
+ * along with the relation's initial contents.
*
*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
- * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.26 2007/01/05 22:19:53 momjian Exp $
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.27 2007/02/14 01:58:58 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -26,23 +29,30 @@
/* ----------------
* pg_trigger definition. cpp turns this into
* typedef struct FormData_pg_trigger
+ *
+ * Note: when tgconstraint is nonzero, tgisconstraint must be true, and
+ * tgconstrname, tgconstrrelid, tgdeferrable, tginitdeferred are redundant
+ * with the referenced pg_constraint entry. The reason we keep these fields
+ * is that we support "stand-alone" constraint triggers with no corresponding
+ * pg_constraint entry.
* ----------------
*/
#define TriggerRelationId 2620
CATALOG(pg_trigger,2620)
{
- Oid tgrelid; /* triggered relation */
- NameData tgname; /* trigger' name */
+ Oid tgrelid; /* relation trigger is attached to */
+ NameData tgname; /* trigger's name */
Oid tgfoid; /* OID of function to be called */
int2 tgtype; /* BEFORE/AFTER UPDATE/DELETE/INSERT
- * ROW/STATEMENT */
+ * ROW/STATEMENT; see below */
bool tgenabled; /* trigger is enabled/disabled */
- bool tgisconstraint; /* trigger is a RI constraint */
- NameData tgconstrname; /* RI constraint name */
- Oid tgconstrrelid; /* RI table of foreign key definition */
- bool tgdeferrable; /* RI trigger is deferrable */
- bool tginitdeferred; /* RI trigger is deferred initially */
+ bool tgisconstraint; /* trigger is a constraint trigger */
+ NameData tgconstrname; /* constraint name */
+ Oid tgconstrrelid; /* constraint's FROM table, if any */
+ Oid tgconstraint; /* owning pg_constraint entry, if any */
+ bool tgdeferrable; /* constraint trigger is deferrable */
+ bool tginitdeferred; /* constraint trigger is deferred initially */
int2 tgnargs; /* # of extra arguments in tgargs */
/* VARIABLE LENGTH FIELDS: */
@@ -61,7 +71,7 @@ typedef FormData_pg_trigger *Form_pg_trigger;
* compiler constants for pg_trigger
* ----------------
*/
-#define Natts_pg_trigger 13
+#define Natts_pg_trigger 14
#define Anum_pg_trigger_tgrelid 1
#define Anum_pg_trigger_tgname 2
#define Anum_pg_trigger_tgfoid 3
@@ -70,18 +80,21 @@ typedef FormData_pg_trigger *Form_pg_trigger;
#define Anum_pg_trigger_tgisconstraint 6
#define Anum_pg_trigger_tgconstrname 7
#define Anum_pg_trigger_tgconstrrelid 8
-#define Anum_pg_trigger_tgdeferrable 9
-#define Anum_pg_trigger_tginitdeferred 10
-#define Anum_pg_trigger_tgnargs 11
-#define Anum_pg_trigger_tgattr 12
-#define Anum_pg_trigger_tgargs 13
+#define Anum_pg_trigger_tgconstraint 9
+#define Anum_pg_trigger_tgdeferrable 10
+#define Anum_pg_trigger_tginitdeferred 11
+#define Anum_pg_trigger_tgnargs 12
+#define Anum_pg_trigger_tgattr 13
+#define Anum_pg_trigger_tgargs 14
+/* Bits within tgtype */
#define TRIGGER_TYPE_ROW (1 << 0)
#define TRIGGER_TYPE_BEFORE (1 << 1)
#define TRIGGER_TYPE_INSERT (1 << 2)
#define TRIGGER_TYPE_DELETE (1 << 3)
#define TRIGGER_TYPE_UPDATE (1 << 4)
+/* Macros for manipulating tgtype */
#define TRIGGER_CLEAR_TYPE(type) ((type) = 0)
#define TRIGGER_SETT_ROW(type) ((type) |= TRIGGER_TYPE_ROW)
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index 284815b47f..ac9eb7296d 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.60 2007/01/05 22:19:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.61 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -78,34 +78,8 @@ typedef struct TriggerData
#define TRIGGER_FIRED_AFTER(event) \
(!TRIGGER_FIRED_BEFORE (event))
-/*
- * RI trigger function arguments are stored in pg_trigger.tgargs bytea
- *
- * constrname\0fkrel\0pkrel\0matchtype\0fkatt\0pkatt\0fkatt\0pkatt\0...
- *
- * There are one or more pairs of fkatt/pkatt names.
- *
- * The relation names are no longer of much use since they are not
- * guaranteed unique; they are present only for backwards compatibility.
- * Use the tgrelid and tgconstrrelid fields to identify the referenced
- * relations, instead. (But note that which is which will depend on which
- * trigger you are looking at!)
- */
-#define RI_CONSTRAINT_NAME_ARGNO 0
-#define RI_FK_RELNAME_ARGNO 1
-#define RI_PK_RELNAME_ARGNO 2
-#define RI_MATCH_TYPE_ARGNO 3
-#define RI_FIRST_ATTNAME_ARGNO 4 /* first attname pair starts
- * here */
-#define RI_KEYPAIR_FK_IDX 0
-#define RI_KEYPAIR_PK_IDX 1
-
-#define RI_MAX_NUMKEYS INDEX_MAX_KEYS
-#define RI_MAX_ARGUMENTS (RI_FIRST_ATTNAME_ARGNO + (RI_MAX_NUMKEYS * 2))
-
-
-extern Oid CreateTrigger(CreateTrigStmt *stmt, bool forConstraint);
+extern Oid CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid);
extern void DropTrigger(Oid relid, const char *trigname,
DropBehavior behavior, bool missing_ok);
@@ -175,10 +149,10 @@ extern bool RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
HeapTuple old_row, HeapTuple new_row);
extern bool RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
HeapTuple old_row, HeapTuple new_row);
-extern bool RI_Initial_Check(FkConstraint *fkconstraint,
- Relation rel,
- Relation pkrel);
+extern bool RI_Initial_Check(Trigger *trigger,
+ Relation fk_rel, Relation pk_rel);
+/* result values for RI_FKey_trigger_type: */
#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */
#define RI_TRIGGER_FK 2 /* is a trigger on the FK relation */
#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 8e9bfaa1ed..78c45891fa 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.116 2007/01/30 01:33:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.117 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,6 +58,7 @@ extern Oid get_atttype(Oid relid, AttrNumber attnum);
extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
Oid *typid, int32 *typmod);
+extern char *get_constraint_name(Oid conoid);
extern Oid get_opclass_family(Oid opclass);
extern Oid get_opclass_input_type(Oid opclass);
extern RegProcedure get_opcode(Oid opno);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 8ce7e118a4..3ac44b11cd 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.96 2007/01/25 02:17:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.97 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,6 +56,7 @@ typedef struct Trigger
bool tgenabled;
bool tgisconstraint;
Oid tgconstrrelid;
+ Oid tgconstraint;
bool tgdeferrable;
bool tginitdeferred;
int16 tgnargs;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index b8e55e57ef..c6967251ce 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.67 2007/01/05 22:19:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.68 2007/02/14 01:58:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,42 +28,43 @@
* Keep them in alphabetical order.
*/
-#define AGGFNOID 0
-#define AMNAME 1
-#define AMOID 2
-#define AMOPOPID 3
-#define AMOPSTRATEGY 4
-#define AMPROCNUM 5
-#define ATTNAME 6
-#define ATTNUM 7
-#define AUTHMEMMEMROLE 8
-#define AUTHMEMROLEMEM 9
-#define AUTHNAME 10
-#define AUTHOID 11
-#define CASTSOURCETARGET 12
-#define CLAAMNAMENSP 13
-#define CLAOID 14
-#define CONDEFAULT 15
-#define CONNAMENSP 16
-#define CONOID 17
-#define DATABASEOID 18
-#define INDEXRELID 19
-#define LANGNAME 20
-#define LANGOID 21
-#define NAMESPACENAME 22
-#define NAMESPACEOID 23
-#define OPERNAMENSP 24
-#define OPEROID 25
-#define OPFAMILYAMNAMENSP 26
-#define OPFAMILYOID 27
-#define PROCNAMEARGSNSP 28
-#define PROCOID 29
-#define RELNAMENSP 30
-#define RELOID 31
-#define RULERELNAME 32
-#define STATRELATT 33
-#define TYPENAMENSP 34
-#define TYPEOID 35
+#define AGGFNOID 0
+#define AMNAME 1
+#define AMOID 2
+#define AMOPOPID 3
+#define AMOPSTRATEGY 4
+#define AMPROCNUM 5
+#define ATTNAME 6
+#define ATTNUM 7
+#define AUTHMEMMEMROLE 8
+#define AUTHMEMROLEMEM 9
+#define AUTHNAME 10
+#define AUTHOID 11
+#define CASTSOURCETARGET 12
+#define CLAAMNAMENSP 13
+#define CLAOID 14
+#define CONDEFAULT 15
+#define CONNAMENSP 16
+#define CONSTROID 17
+#define CONVOID 18
+#define DATABASEOID 19
+#define INDEXRELID 20
+#define LANGNAME 21
+#define LANGOID 22
+#define NAMESPACENAME 23
+#define NAMESPACEOID 24
+#define OPERNAMENSP 25
+#define OPEROID 26
+#define OPFAMILYAMNAMENSP 27
+#define OPFAMILYOID 28
+#define PROCNAMEARGSNSP 29
+#define PROCOID 30
+#define RELNAMENSP 31
+#define RELOID 32
+#define RULERELNAME 33
+#define STATRELATT 34
+#define TYPENAMENSP 35
+#define TYPEOID 36
extern void InitCatalogCache(void);
extern void InitCatalogCachePhase2(void);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index a1a61f710c..11b8c24c3d 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -195,8 +195,9 @@ DROP TABLE tmp2;
-- is run in parallel with foreign_key.sql.
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
+INSERT INTO PKTABLE VALUES(42);
CREATE TEMP TABLE FKTABLE (ftest1 inet);
--- This next should fail, because inet=int does not exist
+-- This next should fail, because int=inet does not exist
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
@@ -205,21 +206,40 @@ DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and i
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
--- This should succeed, even though they are different types
--- because varchar=int does exist
DROP TABLE FKTABLE;
-CREATE TEMP TABLE FKTABLE (ftest1 varchar);
+-- This should succeed, even though they are different types,
+-- because int=int8 exists and is a member of the integer opfamily
+CREATE TEMP TABLE FKTABLE (ftest1 int8);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
-DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
--- As should this
-ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-WARNING: foreign key constraint "fktable_ftest1_fkey1" will require costly sequential scans
-DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
-DROP TABLE pktable cascade;
-NOTICE: drop cascades to constraint fktable_ftest1_fkey1 on table fktable
-NOTICE: drop cascades to constraint fktable_ftest1_fkey on table fktable
-DROP TABLE fktable;
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
+DETAIL: Key (ftest1)=(43) is not present in table "pktable".
+DROP TABLE FKTABLE;
+-- This should fail, because we'd have to cast numeric to int which is
+-- not an implicit coercion (or use numeric=numeric, but that's not part
+-- of the integer opfamily)
+CREATE TEMP TABLE FKTABLE (ftest1 numeric);
+ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
+ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
+DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: numeric and integer.
+DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
+-- On the other hand, this should work because int implicitly promotes to
+-- numeric, and we allow promotion on the FK side
+CREATE TEMP TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
+INSERT INTO PKTABLE VALUES(42);
+CREATE TEMP TABLE FKTABLE (ftest1 int);
+ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
+DETAIL: Key (ftest1)=(43) is not present in table "pktable".
+DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
PRIMARY KEY(ptest1, ptest2));
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 5424731d4d..41c2f39788 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -646,7 +646,7 @@ SELECT * from FKTABLE;
UPDATE PKTABLE set ptest2=5 where ptest2=2;
ERROR: insert or update on table "fktable" violates foreign key constraint "constrname3"
DETAIL: Key (ftest1,ftest2,ftest3)=(1,-1,3) is not present in table "pktable".
-CONTEXT: SQL statement "UPDATE ONLY "public"."fktable" SET "ftest2" = DEFAULT WHERE "ftest1" = $1 AND "ftest2" = $2 AND "ftest3" = $3"
+CONTEXT: SQL statement "UPDATE ONLY "public"."fktable" SET "ftest2" = DEFAULT WHERE $1 OPERATOR(pg_catalog.=) "ftest1" AND $2 OPERATOR(pg_catalog.=) "ftest2" AND $3 OPERATOR(pg_catalog.=) "ftest3""
-- Try to update something that will set default
UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
UPDATE PKTABLE set ptest2=10 where ptest2=4;
@@ -749,7 +749,8 @@ DROP TABLE PKTABLE;
-- Basic one column, two table setup
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
--- This next should fail, because inet=int does not exist
+INSERT INTO PKTABLE VALUES(42);
+-- This next should fail, because int=inet does not exist
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
@@ -758,16 +759,41 @@ DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and i
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
--- This should succeed (with a warning), even though they are different types
--- because int=varchar does exist
-CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
-WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
-DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
+-- This should succeed, even though they are different types,
+-- because int=int8 exists and is a member of the integer opfamily
+CREATE TABLE FKTABLE (ftest1 int8 REFERENCES pktable);
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
+DETAIL: Key (ftest1)=(43) is not present in table "pktable".
+UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
+UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
+ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
+DETAIL: Key (ftest1)=(43) is not present in table "pktable".
DROP TABLE FKTABLE;
--- As should this
-CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
-WARNING: foreign key constraint "fktable_ftest1_fkey" will require costly sequential scans
-DETAIL: Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
+-- This should fail, because we'd have to cast numeric to int which is
+-- not an implicit coercion (or use numeric=numeric, but that's not part
+-- of the integer opfamily)
+CREATE TABLE FKTABLE (ftest1 numeric REFERENCES pktable);
+ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
+DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: numeric and integer.
+DROP TABLE PKTABLE;
+-- On the other hand, this should work because int implicitly promotes to
+-- numeric, and we allow promotion on the FK side
+CREATE TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
+INSERT INTO PKTABLE VALUES(42);
+CREATE TABLE FKTABLE (ftest1 int REFERENCES pktable);
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
+DETAIL: Key (ftest1)=(43) is not present in table "pktable".
+UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
+UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
+ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_ftest1_fkey"
+DETAIL: Key (ftest1)=(43) is not present in table "pktable".
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
-- Two columns, two tables
@@ -1083,21 +1109,24 @@ CREATE TEMP TABLE fktable (
x5 INT2
);
-- check individual constraints with alter table.
--- should generate warnings
+-- should fail
+-- varchar does not promote to real
ALTER TABLE fktable ADD CONSTRAINT fk_2_3
FOREIGN KEY (x2) REFERENCES pktable(id3);
-WARNING: foreign key constraint "fk_2_3" will require costly sequential scans
-DETAIL: Key columns "x2" and "id3" are of different types: character varying and real.
+ERROR: foreign key constraint "fk_2_3" cannot be implemented
+DETAIL: Key columns "x2" and "id3" are of incompatible types: character varying and real.
+-- nor to int4
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
FOREIGN KEY (x2) REFERENCES pktable(id1);
-WARNING: foreign key constraint "fk_2_1" will require costly sequential scans
-DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer.
+ERROR: foreign key constraint "fk_2_1" cannot be implemented
+DETAIL: Key columns "x2" and "id1" are of incompatible types: character varying and integer.
+-- real does not promote to int4
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
FOREIGN KEY (x3) REFERENCES pktable(id1);
-WARNING: foreign key constraint "fk_3_1" will require costly sequential scans
-DETAIL: Key columns "x3" and "id1" are of different types: real and integer.
--- should NOT generate warnings
--- int4 promotes to text, so this is ok
+ERROR: foreign key constraint "fk_3_1" cannot be implemented
+DETAIL: Key columns "x3" and "id1" are of incompatible types: real and integer.
+-- should succeed
+-- int4 promotes to text, so this is allowed (though pretty durn debatable)
ALTER TABLE fktable ADD CONSTRAINT fk_1_2
FOREIGN KEY (x1) REFERENCES pktable(id2);
-- int4 promotes to real
@@ -1106,45 +1135,36 @@ FOREIGN KEY (x1) REFERENCES pktable(id3);
-- text is compatible with varchar
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
FOREIGN KEY (x4) REFERENCES pktable(id2);
--- int2 is part of int4 opclass as of 8.0
+-- int2 is part of integer opfamily as of 8.0
ALTER TABLE fktable ADD CONSTRAINT fk_5_1
FOREIGN KEY (x5) REFERENCES pktable(id1);
-- check multikey cases, especially out-of-order column lists
--- no warnings here
+-- these should work
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
ALTER TABLE fktable ADD CONSTRAINT fk_213_213
FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
--- warnings here
+-- these should fail
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
-WARNING: foreign key constraint "fk_123_231" will require costly sequential scans
-DETAIL: Key columns "x2" and "id3" are of different types: character varying and real.
-WARNING: foreign key constraint "fk_123_231" will require costly sequential scans
-DETAIL: Key columns "x3" and "id1" are of different types: real and integer.
+ERROR: foreign key constraint "fk_123_231" cannot be implemented
+DETAIL: Key columns "x2" and "id3" are of incompatible types: character varying and real.
ALTER TABLE fktable ADD CONSTRAINT fk_241_132
FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
-WARNING: foreign key constraint "fk_241_132" will require costly sequential scans
-DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer.
-WARNING: foreign key constraint "fk_241_132" will require costly sequential scans
-DETAIL: Key columns "x4" and "id3" are of different types: text and real.
+ERROR: foreign key constraint "fk_241_132" cannot be implemented
+DETAIL: Key columns "x2" and "id1" are of incompatible types: character varying and integer.
DROP TABLE pktable, fktable CASCADE;
-NOTICE: drop cascades to constraint fk_241_132 on table fktable
-NOTICE: drop cascades to constraint fk_123_231 on table fktable
NOTICE: drop cascades to constraint fk_253_213 on table fktable
NOTICE: drop cascades to constraint fk_213_213 on table fktable
NOTICE: drop cascades to constraint fk_123_123 on table fktable
NOTICE: drop cascades to constraint fk_5_1 on table fktable
-NOTICE: drop cascades to constraint fk_3_1 on table fktable
-NOTICE: drop cascades to constraint fk_2_1 on table fktable
NOTICE: drop cascades to constraint fktable_x1_fkey on table fktable
NOTICE: drop cascades to constraint fk_4_2 on table fktable
NOTICE: drop cascades to constraint fk_1_2 on table fktable
NOTICE: drop cascades to constraint fktable_x2_fkey on table fktable
NOTICE: drop cascades to constraint fk_1_3 on table fktable
-NOTICE: drop cascades to constraint fk_2_3 on table fktable
NOTICE: drop cascades to constraint fktable_x3_fkey on table fktable
-- test a tricky case: we can elide firing the FK check trigger during
-- an UPDATE if the UPDATE did not change the foreign key
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 2ed67b3a1c..d52d6c822e 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -241,21 +241,40 @@ DROP TABLE tmp2;
-- is run in parallel with foreign_key.sql.
CREATE TEMP TABLE PKTABLE (ptest1 int PRIMARY KEY);
+INSERT INTO PKTABLE VALUES(42);
CREATE TEMP TABLE FKTABLE (ftest1 inet);
--- This next should fail, because inet=int does not exist
+-- This next should fail, because int=inet does not exist
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-- This should also fail for the same reason, but here we
-- give the column name
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
--- This should succeed, even though they are different types
--- because varchar=int does exist
DROP TABLE FKTABLE;
-CREATE TEMP TABLE FKTABLE (ftest1 varchar);
+-- This should succeed, even though they are different types,
+-- because int=int8 exists and is a member of the integer opfamily
+CREATE TEMP TABLE FKTABLE (ftest1 int8);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
--- As should this
-ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-DROP TABLE pktable cascade;
-DROP TABLE fktable;
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+DROP TABLE FKTABLE;
+-- This should fail, because we'd have to cast numeric to int which is
+-- not an implicit coercion (or use numeric=numeric, but that's not part
+-- of the integer opfamily)
+CREATE TEMP TABLE FKTABLE (ftest1 numeric);
+ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
+DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
+-- On the other hand, this should work because int implicitly promotes to
+-- numeric, and we allow promotion on the FK side
+CREATE TEMP TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
+INSERT INTO PKTABLE VALUES(42);
+CREATE TEMP TABLE FKTABLE (ftest1 int);
+ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+DROP TABLE FKTABLE;
+DROP TABLE PKTABLE;
CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
PRIMARY KEY(ptest1, ptest2));
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index 2b22d0cecd..16eee1e754 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -444,17 +444,36 @@ DROP TABLE PKTABLE;
--
-- Basic one column, two table setup
CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
--- This next should fail, because inet=int does not exist
+INSERT INTO PKTABLE VALUES(42);
+-- This next should fail, because int=inet does not exist
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
-- This should also fail for the same reason, but here we
-- give the column name
CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
--- This should succeed (with a warning), even though they are different types
--- because int=varchar does exist
-CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
+-- This should succeed, even though they are different types,
+-- because int=int8 exists and is a member of the integer opfamily
+CREATE TABLE FKTABLE (ftest1 int8 REFERENCES pktable);
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
+UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
DROP TABLE FKTABLE;
--- As should this
-CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
+-- This should fail, because we'd have to cast numeric to int which is
+-- not an implicit coercion (or use numeric=numeric, but that's not part
+-- of the integer opfamily)
+CREATE TABLE FKTABLE (ftest1 numeric REFERENCES pktable);
+DROP TABLE PKTABLE;
+-- On the other hand, this should work because int implicitly promotes to
+-- numeric, and we allow promotion on the FK side
+CREATE TABLE PKTABLE (ptest1 numeric PRIMARY KEY);
+INSERT INTO PKTABLE VALUES(42);
+CREATE TABLE FKTABLE (ftest1 int REFERENCES pktable);
+-- Check it actually works
+INSERT INTO FKTABLE VALUES(42); -- should succeed
+INSERT INTO FKTABLE VALUES(43); -- should fail
+UPDATE FKTABLE SET ftest1 = ftest1; -- should succeed
+UPDATE FKTABLE SET ftest1 = ftest1 + 1; -- should fail
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
@@ -727,20 +746,23 @@ CREATE TEMP TABLE fktable (
-- check individual constraints with alter table.
--- should generate warnings
+-- should fail
+-- varchar does not promote to real
ALTER TABLE fktable ADD CONSTRAINT fk_2_3
FOREIGN KEY (x2) REFERENCES pktable(id3);
+-- nor to int4
ALTER TABLE fktable ADD CONSTRAINT fk_2_1
FOREIGN KEY (x2) REFERENCES pktable(id1);
+-- real does not promote to int4
ALTER TABLE fktable ADD CONSTRAINT fk_3_1
FOREIGN KEY (x3) REFERENCES pktable(id1);
--- should NOT generate warnings
+-- should succeed
--- int4 promotes to text, so this is ok
+-- int4 promotes to text, so this is allowed (though pretty durn debatable)
ALTER TABLE fktable ADD CONSTRAINT fk_1_2
FOREIGN KEY (x1) REFERENCES pktable(id2);
@@ -752,13 +774,13 @@ FOREIGN KEY (x1) REFERENCES pktable(id3);
ALTER TABLE fktable ADD CONSTRAINT fk_4_2
FOREIGN KEY (x4) REFERENCES pktable(id2);
--- int2 is part of int4 opclass as of 8.0
+-- int2 is part of integer opfamily as of 8.0
ALTER TABLE fktable ADD CONSTRAINT fk_5_1
FOREIGN KEY (x5) REFERENCES pktable(id1);
-- check multikey cases, especially out-of-order column lists
--- no warnings here
+-- these should work
ALTER TABLE fktable ADD CONSTRAINT fk_123_123
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
@@ -769,7 +791,7 @@ FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
ALTER TABLE fktable ADD CONSTRAINT fk_253_213
FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
--- warnings here
+-- these should fail
ALTER TABLE fktable ADD CONSTRAINT fk_123_231
FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);