diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index 368b1dea3e..f6cfbb68f6 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -237,6 +237,7 @@ sub ParseData
 	# Scan the input file.
 	while (<$ifd>)
 	{
+    next if /^#/;
 		my $hash_ref;
 
 		if (/{/)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index b7bcdd9d0f..c1346d765c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2399,6 +2399,7 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
 							  is_local, /* conislocal */
 							  inhcount, /* coninhcount */
 							  is_no_inherit,	/* connoinherit */
+							  false,	/* contemporal */
 							  is_internal); /* internally constructed? */
 
 	pfree(ccbin);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 99ae159f98..41645ec692 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1832,6 +1832,7 @@ index_constraint_create(Relation heapRelation,
 								   islocal,
 								   inhcount,
 								   noinherit,
+								   false,	/* contemporal */
 								   is_internal);
 
 	/*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index b6145593a3..e913741103 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -76,6 +76,7 @@ CreateConstraintEntry(const char *constraintName,
 					  bool conIsLocal,
 					  int conInhCount,
 					  bool conNoInherit,
+					  bool conTemporal,
 					  bool is_internal)
 {
 	Relation	conDesc;
@@ -184,6 +185,7 @@ CreateConstraintEntry(const char *constraintName,
 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
 	values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
 	values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
+	values[Anum_pg_constraint_contemporal - 1] = BoolGetDatum(conTemporal);
 
 	if (conkeyArray)
 		values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index fb2be10794..217d7f385c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -328,9 +328,12 @@ static int	transformColumnNameList(Oid relId, List *colList,
 static int	transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
 									   List **attnamelist,
 									   int16 *attnums, Oid *atttypids,
+										 Node **periodattname,
+										 int16 *periodattnums, Oid *periodatttypids,
 									   Oid *opclasses);
 static Oid	transformFkeyCheckAttrs(Relation pkrel,
 									int numattrs, int16 *attnums,
+									bool is_temporal, int16 *periodattnums,
 									Oid *opclasses);
 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
@@ -338,7 +341,7 @@ static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
 static void validateCheckConstraint(Relation rel, HeapTuple constrtup);
 static void validateForeignKeyConstraint(char *conname,
 										 Relation rel, Relation pkrel,
-										 Oid pkindOid, Oid constraintOid);
+										 Oid pkindOid, Oid constraintOid, bool temporal);
 static void ATController(AlterTableStmt *parsetree,
 						 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode);
 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
@@ -424,12 +427,12 @@ static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstra
 											Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
 											int numfks, int16 *pkattnum, int16 *fkattnum,
 											Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
-											bool old_check_ok);
+											bool old_check_ok, bool is_temporal);
 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
 									Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
 									int numfks, int16 *pkattnum, int16 *fkattnum,
 									Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
-									bool old_check_ok, LOCKMODE lockmode);
+									bool old_check_ok, bool is_temporal, LOCKMODE lockmode);
 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
 									   Relation partitionRel);
 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
@@ -446,6 +449,12 @@ static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
 										 Oid parentConstrOid, int numfks,
 										 AttrNumber *mapped_conkey, AttrNumber *confkey,
 										 Oid *conpfeqop);
+static void FindFKComparisonOperators(Constraint *fkconstraint,
+					AlteredTableInfo *tab, int i, int16 *fkattnum,
+					bool *old_check_ok, ListCell **old_pfeqop_item,
+					Oid pktype, Oid fktype, Oid opclass,
+					bool is_temporal, bool for_overlaps,
+					Oid *pfeqopOut, Oid *ppeqopOut, Oid *ffeqopOut);
 static void ATExecDropConstraint(Relation rel, const char *constrName,
 								 DropBehavior behavior,
 								 bool recurse, bool recursing,
@@ -4709,7 +4718,8 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 
 				validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
 											 con->refindid,
-											 con->conid);
+											 con->conid,
+											 fkconstraint->fk_period != NULL);
 
 				/*
 				 * No need to mark the constraint row as validated, we did
@@ -7604,6 +7614,11 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	Oid			pfeqoperators[INDEX_MAX_KEYS];
 	Oid			ppeqoperators[INDEX_MAX_KEYS];
 	Oid			ffeqoperators[INDEX_MAX_KEYS];
+	bool		is_temporal = (fkconstraint->fk_period != NULL);
+	int16		pkperiodattnum[1];
+	int16		fkperiodattnum[1];
+	Oid			pkperiodtypoid[1];
+	Oid			fkperiodtypoid[1];
 	int			i;
 	int			numfks,
 				numpks;
@@ -7706,6 +7721,18 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	numfks = transformColumnNameList(RelationGetRelid(rel),
 									 fkconstraint->fk_attrs,
 									 fkattnum, fktypoid);
+	if (is_temporal)
+	{
+	  List *fk_period;
+	  MemSet(pkperiodattnum, 0, sizeof(pkperiodattnum));
+	  MemSet(fkperiodattnum, 0, sizeof(fkperiodattnum));
+	  MemSet(pkperiodtypoid, 0, sizeof(pkperiodtypoid));
+	  MemSet(fkperiodtypoid, 0, sizeof(fkperiodtypoid));
+	  fk_period = list_make1(fkconstraint->fk_period);
+	  transformColumnNameList(RelationGetRelid(rel),
+							  fk_period,
+							  fkperiodattnum, fkperiodtypoid);
+	}
 
 	/*
 	 * If the attribute list for the referenced table was omitted, lookup the
@@ -7718,6 +7745,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
 											&fkconstraint->pk_attrs,
 											pkattnum, pktypoid,
+											&fkconstraint->pk_period,
+											pkperiodattnum, pkperiodtypoid,
 											opclasses);
 	}
 	else
@@ -7725,8 +7754,15 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		numpks = transformColumnNameList(RelationGetRelid(pkrel),
 										 fkconstraint->pk_attrs,
 										 pkattnum, pktypoid);
+		if (is_temporal) {
+			List *pk_period = list_make1(fkconstraint->pk_period);
+			transformColumnNameList(RelationGetRelid(pkrel),
+									pk_period,
+									pkperiodattnum, pkperiodtypoid);
+		}
 		/* Look for an index matching the column list */
 		indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
+										   is_temporal, pkperiodattnum,
 										   opclasses);
 	}
 
@@ -7776,6 +7812,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 				(errcode(ERRCODE_INVALID_FOREIGN_KEY),
 				 errmsg("number of referencing and referenced columns for foreign key disagree")));
 
+	// TODO: Need a check that if one side has a PERIOD the other does too
+
 	/*
 	 * On the strength of a previous constraint, we might avoid scanning
 	 * tables to validate this one.  See below.
@@ -7785,188 +7823,29 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	for (i = 0; i < numpks; i++)
 	{
-		Oid			pktype = pktypoid[i];
-		Oid			fktype = fktypoid[i];
-		Oid			fktyped;
-		HeapTuple	cla_ht;
-		Form_pg_opclass cla_tup;
-		Oid			amid;
-		Oid			opfamily;
-		Oid			opcintype;
-		Oid			pfeqop;
-		Oid			ppeqop;
-		Oid			ffeqop;
-		int16		eqstrategy;
-		Oid			pfeqop_right;
-
-		/* We need several fields out of the pg_opclass entry */
-		cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
-		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;
-		opcintype = 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 primary equality operator for the index.
-		 * We'll use it for PK = PK comparisons.
-		 */
-		ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
-									 eqstrategy);
-
-		if (!OidIsValid(ppeqop))
-			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
-				 eqstrategy, opcintype, opcintype, opfamily);
-
-		/*
-		 * Are there equality operators that take exactly the FK type? Assume
-		 * we should look through any domain here.
-		 */
-		fktyped = getBaseType(fktype);
-
-		pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
-									 eqstrategy);
-		if (OidIsValid(pfeqop))
-		{
-			pfeqop_right = fktyped;
-			ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
-										 eqstrategy);
-		}
-		else
-		{
-			/* keep compiler quiet */
-			pfeqop_right = InvalidOid;
-			ffeqop = InvalidOid;
-		}
-
-		if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
-		{
-			/*
-			 * Otherwise, look for an implicit cast from the FK type to the
-			 * opcintype, and if found, use the primary equality operator.
-			 * This is a bit tricky because opcintype might be a polymorphic
-			 * type such as ANYARRAY or ANYENUM; so what we have to test is
-			 * whether the two actual column types can be concurrently cast to
-			 * that type.  (Otherwise, we'd fail to reject combinations such
-			 * as int[] and point[].)
-			 */
-			Oid			input_typeids[2];
-			Oid			target_typeids[2];
-
-			input_typeids[0] = pktype;
-			input_typeids[1] = fktype;
-			target_typeids[0] = opcintype;
-			target_typeids[1] = opcintype;
-			if (can_coerce_type(2, input_typeids, target_typeids,
-								COERCION_IMPLICIT))
-			{
-				pfeqop = ffeqop = ppeqop;
-				pfeqop_right = opcintype;
-			}
-		}
-
-		if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
-			ereport(ERROR,
-					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("foreign key constraint \"%s\" cannot be implemented",
-							fkconstraint->conname),
-					 errdetail("Key columns \"%s\" and \"%s\" "
-							   "are of incompatible types: %s and %s.",
-							   strVal(list_nth(fkconstraint->fk_attrs, i)),
-							   strVal(list_nth(fkconstraint->pk_attrs, i)),
-							   format_type_be(fktype),
-							   format_type_be(pktype))));
-
-		if (old_check_ok)
-		{
-			/*
-			 * When a pfeqop changes, revalidate the constraint.  We could
-			 * permit intra-opfamily changes, but that adds subtle complexity
-			 * without any concrete benefit for core types.  We need not
-			 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
-			 */
-			old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
-			old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
-									old_pfeqop_item);
-		}
-		if (old_check_ok)
-		{
-			Oid			old_fktype;
-			Oid			new_fktype;
-			CoercionPathType old_pathtype;
-			CoercionPathType new_pathtype;
-			Oid			old_castfunc;
-			Oid			new_castfunc;
-			Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
-												   fkattnum[i] - 1);
-
-			/*
-			 * Identify coercion pathways from each of the old and new FK-side
-			 * column types to the right (foreign) operand type of the pfeqop.
-			 * We may assume that pg_constraint.conkey is not changing.
-			 */
-			old_fktype = attr->atttypid;
-			new_fktype = fktype;
-			old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
-										&old_castfunc);
-			new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
-										&new_castfunc);
-
-			/*
-			 * Upon a change to the cast from the FK column to its pfeqop
-			 * operand, revalidate the constraint.  For this evaluation, a
-			 * binary coercion cast is equivalent to no cast at all.  While
-			 * type implementors should design implicit casts with an eye
-			 * toward consistency of operations like equality, we cannot
-			 * assume here that they have done so.
-			 *
-			 * A function with a polymorphic argument could change behavior
-			 * arbitrarily in response to get_fn_expr_argtype().  Therefore,
-			 * when the cast destination is polymorphic, we only avoid
-			 * revalidation if the input type has not changed at all.  Given
-			 * just the core data types and operator classes, this requirement
-			 * prevents no would-be optimizations.
-			 *
-			 * If the cast converts from a base type to a domain thereon, then
-			 * that domain type must be the opcintype of the unique index.
-			 * Necessarily, the primary key column must then be of the domain
-			 * type.  Since the constraint was previously valid, all values on
-			 * the foreign side necessarily exist on the primary side and in
-			 * turn conform to the domain.  Consequently, we need not treat
-			 * domains specially here.
-			 *
-			 * Since we require that all collations share the same notion of
-			 * equality (which they do, because texteq reduces to bitwise
-			 * equality), we don't compare collation here.
-			 *
-			 * We need not directly consider the PK type.  It's necessarily
-			 * binary coercible to the opcintype of the unique index column,
-			 * and ri_triggers.c will only deal with PK datums in terms of
-			 * that opcintype.  Changing the opcintype also changes pfeqop.
-			 */
-			old_check_ok = (new_pathtype == old_pathtype &&
-							new_castfunc == old_castfunc &&
-							(!IsPolymorphicType(pfeqop_right) ||
-							 new_fktype == old_fktype));
-		}
-
-		pfeqoperators[i] = pfeqop;
-		ppeqoperators[i] = ppeqop;
-		ffeqoperators[i] = ffeqop;
+		FindFKComparisonOperators(
+				fkconstraint, tab, i, fkattnum,
+				&old_check_ok, &old_pfeqop_item,
+				pktypoid[i], fktypoid[i], opclasses[i],
+				is_temporal, false,
+				&pfeqoperators[i], &ppeqoperators[i], &ffeqoperators[i]);
 	}
+	if (is_temporal)
+	{
+		pkattnum[numpks] = pkperiodattnum[0];
+		fkattnum[numpks] = fkperiodattnum[0];
+		pktypoid[numpks] = pkperiodtypoid[0];
+		fktypoid[numpks] = fkperiodtypoid[0];
+
+		FindFKComparisonOperators(
+				fkconstraint, tab, numpks, fkattnum,
+				&old_check_ok, &old_pfeqop_item,
+				pkperiodtypoid[0], fkperiodtypoid[0], opclasses[numpks],
+				is_temporal, true,
+				&pfeqoperators[numpks], &ppeqoperators[numpks], &ffeqoperators[numpks]);
+		numfks += 1;
+		numpks += 1;
+ 	}
 
 	/*
 	 * Create all the constraint and trigger objects, recursing to partitions
@@ -7981,7 +7860,8 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 									 pfeqoperators,
 									 ppeqoperators,
 									 ffeqoperators,
-									 old_check_ok);
+									 old_check_ok,
+									 is_temporal);
 
 	/* Now handle the referencing side. */
 	addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
@@ -7994,6 +7874,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 							ppeqoperators,
 							ffeqoperators,
 							old_check_ok,
+							is_temporal,
 							lockmode);
 
 	/*
@@ -8034,7 +7915,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 					   Relation pkrel, Oid indexOid, Oid parentConstr,
 					   int numfks,
 					   int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
-					   Oid *ppeqoperators, Oid *ffeqoperators, bool old_check_ok)
+					   Oid *ppeqoperators, Oid *ffeqoperators, bool old_check_ok,
+					   bool is_temporal)
 {
 	ObjectAddress address;
 	Oid			constrOid;
@@ -8110,12 +7992,13 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 									  fkconstraint->fk_upd_action,
 									  fkconstraint->fk_del_action,
 									  fkconstraint->fk_matchtype,
-									  NULL, /* no exclusion constraint */
+									  NULL,
 									  NULL, /* no check constraint */
 									  NULL,
 									  conislocal,	/* islocal */
 									  coninhcount,	/* inhcount */
 									  connoinherit, /* conNoInherit */
+									  is_temporal,
 									  false);	/* is_internal */
 
 	ObjectAddressSet(address, ConstraintRelationId, constrOid);
@@ -8191,7 +8074,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 								   partIndexId, constrOid, numfks,
 								   mapped_pkattnum, fkattnum,
 								   pfeqoperators, ppeqoperators, ffeqoperators,
-								   old_check_ok);
+								   old_check_ok, is_temporal);
 
 			/* Done -- clean up (but keep the lock) */
 			table_close(partRel, NoLock);
@@ -8240,7 +8123,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 						Relation pkrel, Oid indexOid, Oid parentConstr,
 						int numfks, int16 *pkattnum, int16 *fkattnum,
 						Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
-						bool old_check_ok, LOCKMODE lockmode)
+						bool old_check_ok, bool is_temporal, LOCKMODE lockmode)
 {
 	AssertArg(OidIsValid(parentConstr));
 
@@ -8386,6 +8269,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 									  false,
 									  1,
 									  false,
+									  is_temporal,
 									  false);
 
 			/*
@@ -8412,6 +8296,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 									ppeqoperators,
 									ffeqoperators,
 									old_check_ok,
+									is_temporal,
 									lockmode);
 
 			table_close(partition, NoLock);
@@ -8544,6 +8429,7 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
 								   conpfeqop,
 								   conppeqop,
 								   conffeqop);
+
 		for (int i = 0; i < numfks; i++)
 			mapped_confkey[i] = attmap[confkey[i] - 1];
 
@@ -8589,7 +8475,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
 							   conpfeqop,
 							   conppeqop,
 							   conffeqop,
-							   true);
+							   true,
+							   constrForm->contemporal);
 
 		table_close(fkRel, NoLock);
 		ReleaseSysCache(tuple);
@@ -8783,6 +8670,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 								  false,	/* islocal */
 								  1,	/* inhcount */
 								  false,	/* conNoInherit */
+								  constrForm->contemporal,
 								  true);
 
 		/* Set up partition dependencies for the new constraint */
@@ -8812,11 +8700,214 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 								conppeqop,
 								conffeqop,
 								false,	/* no old check exists */
+								constrForm->contemporal,
 								AccessExclusiveLock);
 		table_close(pkrel, NoLock);
 	}
 }
 
+static void
+FindFKComparisonOperators(Constraint *fkconstraint,
+		AlteredTableInfo *tab,
+		int i,
+		int16 *fkattnum,
+		bool *old_check_ok,
+		ListCell **old_pfeqop_item,
+		Oid pktype, Oid fktype, Oid opclass,
+		bool is_temporal, bool for_overlaps,
+		Oid *pfeqopOut, Oid *ppeqopOut, Oid *ffeqopOut)
+{
+	Oid			fktyped;
+	HeapTuple	cla_ht;
+	Form_pg_opclass cla_tup;
+	Oid			amid;
+	Oid			opfamily;
+	Oid			opcintype;
+	Oid			pfeqop;
+	Oid			ppeqop;
+	Oid			ffeqop;
+	int16		eqstrategy;
+	Oid			pfeqop_right;
+
+	/* We need several fields out of the pg_opclass entry */
+	cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
+	if (!HeapTupleIsValid(cla_ht))
+		elog(ERROR, "cache lookup failed for opclass %u", opclass);
+	cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+	amid = cla_tup->opcmethod;
+	opfamily = cla_tup->opcfamily;
+	opcintype = cla_tup->opcintype;
+	ReleaseSysCache(cla_ht);
+
+	if (is_temporal)
+	{
+		if (amid != GIST_AM_OID)
+			elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
+		eqstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
+	}
+	else
+	{
+		/*
+		 * 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 primary equality operator for the index.
+	 * We'll use it for PK = PK comparisons.
+	 */
+	ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
+								 eqstrategy);
+
+	if (!OidIsValid(ppeqop))
+		elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+			 eqstrategy, opcintype, opcintype, opfamily);
+
+	/*
+	 * Are there equality operators that take exactly the FK type? Assume
+	 * we should look through any domain here.
+	 */
+	fktyped = getBaseType(fktype);
+
+	pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
+								 eqstrategy);
+	if (OidIsValid(pfeqop))
+	{
+		pfeqop_right = fktyped;
+		ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
+									 eqstrategy);
+	}
+	else
+	{
+		/* keep compiler quiet */
+		pfeqop_right = InvalidOid;
+		ffeqop = InvalidOid;
+	}
+
+	if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
+	{
+		/*
+		 * Otherwise, look for an implicit cast from the FK type to the
+		 * opcintype, and if found, use the primary equality operator.
+		 * This is a bit tricky because opcintype might be a polymorphic
+		 * type such as ANYARRAY or ANYENUM; so what we have to test is
+		 * whether the two actual column types can be concurrently cast to
+		 * that type.  (Otherwise, we'd fail to reject combinations such
+		 * as int[] and point[].)
+		 */
+		Oid			input_typeids[2];
+		Oid			target_typeids[2];
+
+		input_typeids[0] = pktype;
+		input_typeids[1] = fktype;
+		target_typeids[0] = opcintype;
+		target_typeids[1] = opcintype;
+		if (can_coerce_type(2, input_typeids, target_typeids,
+							COERCION_IMPLICIT))
+		{
+			pfeqop = ffeqop = ppeqop;
+			pfeqop_right = opcintype;
+		}
+	}
+
+	if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("foreign key constraint \"%s\" cannot be implemented",
+						fkconstraint->conname),
+				 errdetail("Key columns \"%s\" and \"%s\" "
+						   "are of incompatible types: %s and %s.",
+						   strVal(list_nth(fkconstraint->fk_attrs, i)),
+						   strVal(list_nth(fkconstraint->pk_attrs, i)),
+						   format_type_be(fktype),
+						   format_type_be(pktype))));
+
+	if (*old_check_ok)
+	{
+		/*
+		 * When a pfeqop changes, revalidate the constraint.  We could
+		 * permit intra-opfamily changes, but that adds subtle complexity
+		 * without any concrete benefit for core types.  We need not
+		 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
+		 */
+		*old_check_ok = (pfeqop == lfirst_oid(*old_pfeqop_item));
+		*old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
+								*old_pfeqop_item);
+	}
+	if (*old_check_ok)
+	{
+		Oid			old_fktype;
+		Oid			new_fktype;
+		CoercionPathType old_pathtype;
+		CoercionPathType new_pathtype;
+		Oid			old_castfunc;
+		Oid			new_castfunc;
+		Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
+											   fkattnum[i] - 1);
+
+		/*
+		 * Identify coercion pathways from each of the old and new FK-side
+		 * column types to the right (foreign) operand type of the pfeqop.
+		 * We may assume that pg_constraint.conkey is not changing.
+		 */
+		old_fktype = attr->atttypid;
+		new_fktype = fktype;
+		old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
+									&old_castfunc);
+		new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
+									&new_castfunc);
+
+		/*
+		 * Upon a change to the cast from the FK column to its pfeqop
+		 * operand, revalidate the constraint.  For this evaluation, a
+		 * binary coercion cast is equivalent to no cast at all.  While
+		 * type implementors should design implicit casts with an eye
+		 * toward consistency of operations like equality, we cannot
+		 * assume here that they have done so.
+		 *
+		 * A function with a polymorphic argument could change behavior
+		 * arbitrarily in response to get_fn_expr_argtype().  Therefore,
+		 * when the cast destination is polymorphic, we only avoid
+		 * revalidation if the input type has not changed at all.  Given
+		 * just the core data types and operator classes, this requirement
+		 * prevents no would-be optimizations.
+		 *
+		 * If the cast converts from a base type to a domain thereon, then
+		 * that domain type must be the opcintype of the unique index.
+		 * Necessarily, the primary key column must then be of the domain
+		 * type.  Since the constraint was previously valid, all values on
+		 * the foreign side necessarily exist on the primary side and in
+		 * turn conform to the domain.  Consequently, we need not treat
+		 * domains specially here.
+		 *
+		 * Since we require that all collations share the same notion of
+		 * equality (which they do, because texteq reduces to bitwise
+		 * equality), we don't compare collation here.
+		 *
+		 * We need not directly consider the PK type.  It's necessarily
+		 * binary coercible to the opcintype of the unique index column,
+		 * and ri_triggers.c will only deal with PK datums in terms of
+		 * that opcintype.  Changing the opcintype also changes pfeqop.
+		 */
+		*old_check_ok = (new_pathtype == old_pathtype &&
+						new_castfunc == old_castfunc &&
+						(!IsPolymorphicType(pfeqop_right) ||
+						 new_fktype == old_fktype));
+
+	}
+
+	*pfeqopOut = pfeqop;
+	*ppeqopOut = ppeqop;
+	*ffeqopOut = ffeqop;
+}
+
 /*
  * When the parent of a partition receives [the referencing side of] a foreign
  * key, we must propagate that foreign key to the partition.  However, the
@@ -9197,7 +9288,7 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
 
 			validateForeignKeyConstraint(constrName, rel, refrel,
 										 con->conindid,
-										 con->oid);
+										 con->oid, con->contemporal);
 			table_close(refrel, NoLock);
 
 			/*
@@ -9330,10 +9421,12 @@ transformColumnNameList(Oid relId, List *colList,
  *
  *	Look up the names, attnums, and types of the primary key attributes
  *	for the pkrel.  Also return the index OID and index opclasses of the
- *	index supporting the primary key.
+ *	index supporting the primary key.  If this is a temporal primary key,
+ *	also set the WITHOUT OVERLAPS attribute name, attnum, and atttypid.
  *
  *	All parameters except pkrel are output parameters.  Also, the function
- *	return value is the number of attributes in the primary key.
+ *	return value is the number of attributes in the primary key,
+ *	not including the WITHOUT OVERLAPS if any.
  *
  *	Used when the column list in the REFERENCES specification is omitted.
  */
@@ -9341,6 +9434,8 @@ static int
 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
 						   List **attnamelist,
 						   int16 *attnums, Oid *atttypids,
+						   Node **periodattname,
+						   int16 *periodattnums, Oid *periodatttypids,
 						   Oid *opclasses)
 {
 	List	   *indexoidlist;
@@ -9408,35 +9503,50 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
 	/*
 	 * Now build the list of PK attributes from the indkey definition (we
 	 * assume a primary key cannot have expressional elements)
+	 * TODO: range expressions will be how we support PERIODs though.
 	 */
 	*attnamelist = NIL;
 	for (i = 0; i < indexStruct->indnkeyatts; i++)
 	{
 		int			pkattno = indexStruct->indkey.values[i];
 
-		attnums[i] = pkattno;
-		atttypids[i] = attnumTypeId(pkrel, pkattno);
-		opclasses[i] = indclass->values[i];
-		*attnamelist = lappend(*attnamelist,
-							   makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
+		if (i == indexStruct->indnkeyatts - 1 && indexStruct->indisexclusion)
+		{
+			periodattnums[i] = pkattno;
+			periodatttypids[i] = attnumTypeId(pkrel, pkattno);
+			opclasses[i] = indclass->values[i];
+			*periodattname = (Node *)makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno))));
+		}
+		else
+		{
+			attnums[i] = pkattno;
+			atttypids[i] = attnumTypeId(pkrel, pkattno);
+			opclasses[i] = indclass->values[i];
+			*attnamelist = lappend(*attnamelist,
+								  makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
+		}
 	}
 
 	ReleaseSysCache(indexTuple);
 
-	return i;
+	if (indexStruct->indisexclusion) return i - 1;
+	else return i;
 }
 
 /*
  * transformFkeyCheckAttrs -
  *
  *	Make sure that the attributes of a referenced table belong to a unique
- *	(or primary key) constraint.  Return the OID of the index supporting
- *	the constraint, as well as the opclasses associated with the index
+ *	(or primary key) constraint.  Or if this is a temporal foreign key
+ *	the primary key should be an exclusion constraint instead.
+ *	Return the OID of the index supporting the constraint,
+ *	as well as the opclasses associated with the index
  *	columns.
  */
 static Oid
 transformFkeyCheckAttrs(Relation pkrel,
 						int numattrs, int16 *attnums,
+						bool is_temporal, int16 *periodattnums,
 						Oid *opclasses) /* output parameter */
 {
 	Oid			indexoid = InvalidOid;
@@ -9463,6 +9573,10 @@ transformFkeyCheckAttrs(Relation pkrel,
 						(errcode(ERRCODE_INVALID_FOREIGN_KEY),
 						 errmsg("foreign key referenced-columns list must not contain duplicates")));
 		}
+		if (is_temporal && attnums[i] == periodattnums[0])
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_FOREIGN_KEY),
+					 errmsg("foreign key referenced-columns list must not contain duplicates")));
 	}
 
 	/*
@@ -9484,12 +9598,16 @@ transformFkeyCheckAttrs(Relation pkrel,
 		indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
 
 		/*
-		 * Must have the right number of columns; must be unique and not a
+		 * Must have the right number of columns; must be unique
+		 * (or if temporal then exclusion instead) and not a
 		 * partial index; forget it if there are any expressions, too. Invalid
 		 * indexes are out as well.
 		 */
-		if (indexStruct->indnkeyatts == numattrs &&
-			indexStruct->indisunique &&
+		if ((is_temporal
+			  ? (indexStruct->indnkeyatts == numattrs + 1 &&
+				 indexStruct->indisexclusion)
+			  : (indexStruct->indnkeyatts == numattrs &&
+				 indexStruct->indisunique)) &&
 			indexStruct->indisvalid &&
 			heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
 			heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
@@ -9529,6 +9647,19 @@ transformFkeyCheckAttrs(Relation pkrel,
 				if (!found)
 					break;
 			}
+			if (is_temporal)
+			{
+				found = false;
+				for (j = 0; j < numattrs + 1; j++)
+				{
+					if (periodattnums[0] == indexStruct->indkey.values[j])
+					{
+						opclasses[numattrs] = indclass->values[j];
+						found = true;
+						break;
+					}
+				}
+			}
 
 			/*
 			 * Refuse to use a deferrable unique/primary key.  This is per SQL
@@ -9718,7 +9849,8 @@ validateForeignKeyConstraint(char *conname,
 							 Relation rel,
 							 Relation pkrel,
 							 Oid pkindOid,
-							 Oid constraintOid)
+							 Oid constraintOid,
+							 bool temporal)
 {
 	TupleTableSlot *slot;
 	TableScanDesc scan;
@@ -9748,8 +9880,10 @@ validateForeignKeyConstraint(char *conname,
 	/*
 	 * 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.
+	 * We can't do a LEFT JOIN for temporal FKs yet,
+	 * but we can once we support temporal left joins.
 	 */
-	if (RI_Initial_Check(&trig, rel, pkrel))
+	if (!temporal && RI_Initial_Check(&trig, rel, pkrel))
 		return;
 
 	/*
@@ -9811,6 +9945,7 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
 					 Oid constraintOid, Oid indexOid, bool on_insert)
 {
 	CreateTrigStmt *fk_trigger;
+	bool is_temporal = fkconstraint->fk_period;
 
 	/*
 	 * Note: for a self-referential FK (referencing and referenced tables are
@@ -9822,7 +9957,10 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
 	 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
 	 */
 	fk_trigger = makeNode(CreateTrigStmt);
-	fk_trigger->trigname = "RI_ConstraintTrigger_c";
+	if (is_temporal)
+		fk_trigger->trigname = "TRI_ConstraintTrigger_c";
+	else
+		fk_trigger->trigname = "RI_ConstraintTrigger_c";
 	fk_trigger->relation = NULL;
 	fk_trigger->row = true;
 	fk_trigger->timing = TRIGGER_TYPE_AFTER;
@@ -9830,12 +9968,18 @@ CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
 	/* Either ON INSERT or ON UPDATE */
 	if (on_insert)
 	{
-		fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
+		if (is_temporal)
+			fk_trigger->funcname = SystemFuncName("TRI_FKey_check_ins");
+		else
+			fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
 		fk_trigger->events = TRIGGER_TYPE_INSERT;
 	}
 	else
 	{
-		fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
+		if (is_temporal)
+			fk_trigger->funcname = SystemFuncName("TRI_FKey_check_upd");
+		else
+			fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
 		fk_trigger->events = TRIGGER_TYPE_UPDATE;
 	}
 
@@ -9881,6 +10025,13 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
 	fk_trigger->whenClause = NULL;
 	fk_trigger->isconstraint = true;
 	fk_trigger->constrrel = NULL;
+	
+	if ((fkconstraint->fk_period != NULL) &&
+			((fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION) || 
+			(fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT)))
+		elog(ERROR, "unrecognized FK action type: %d",
+				(int) fkconstraint->fk_del_action);
+	
 	switch (fkconstraint->fk_del_action)
 	{
 		case FKCONSTR_ACTION_NOACTION:
@@ -9910,7 +10061,7 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
 			break;
 		default:
 			elog(ERROR, "unrecognized FK action type: %d",
-				 (int) fkconstraint->fk_del_action);
+					 (int) fkconstraint->fk_del_action);
 			break;
 	}
 	fk_trigger->args = NIL;
@@ -9937,37 +10088,78 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
 	fk_trigger->whenClause = NULL;
 	fk_trigger->isconstraint = true;
 	fk_trigger->constrrel = NULL;
-	switch (fkconstraint->fk_upd_action)
+	if (fkconstraint->fk_period != NULL)
 	{
-		case FKCONSTR_ACTION_NOACTION:
-			fk_trigger->deferrable = fkconstraint->deferrable;
-			fk_trigger->initdeferred = fkconstraint->initdeferred;
-			fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
-			break;
-		case FKCONSTR_ACTION_RESTRICT:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
-			fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
-			break;
-		case FKCONSTR_ACTION_CASCADE:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
-			fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
-			break;
-		case FKCONSTR_ACTION_SETNULL:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
-			fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
-			break;
-		case FKCONSTR_ACTION_SETDEFAULT:
-			fk_trigger->deferrable = false;
-			fk_trigger->initdeferred = false;
-			fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
-			break;
-		default:
-			elog(ERROR, "unrecognized FK action type: %d",
-				 (int) fkconstraint->fk_upd_action);
-			break;
+		/* Temporal foreign keys */
+		switch (fkconstraint->fk_upd_action)
+		{
+			case FKCONSTR_ACTION_NOACTION:
+				fk_trigger->deferrable = fkconstraint->deferrable;
+				fk_trigger->initdeferred = fkconstraint->initdeferred;
+				fk_trigger->funcname = SystemFuncName("TRI_FKey_noaction_upd");
+				break;
+			case FKCONSTR_ACTION_RESTRICT:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("TRI_FKey_restrict_upd");
+				break;
+			/*
+			case FKCONSTR_ACTION_CASCADE:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("TRI_FKey_cascade_upd");
+				break;
+			case FKCONSTR_ACTION_SETNULL:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("TRI_FKey_setnull_upd");
+				break;
+			case FKCONSTR_ACTION_SETDEFAULT:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("TRI_FKey_setdefault_upd");
+				break;
+			*/
+			default:
+				elog(ERROR, "unrecognized FK action type: %d",
+					 (int) fkconstraint->fk_upd_action);
+				break;
+		}
+	}
+	else
+	{
+		switch (fkconstraint->fk_upd_action)
+		{
+			case FKCONSTR_ACTION_NOACTION:
+				fk_trigger->deferrable = fkconstraint->deferrable;
+				fk_trigger->initdeferred = fkconstraint->initdeferred;
+				fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
+				break;
+			case FKCONSTR_ACTION_RESTRICT:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
+				break;
+			case FKCONSTR_ACTION_CASCADE:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
+				break;
+			case FKCONSTR_ACTION_SETNULL:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
+				break;
+			case FKCONSTR_ACTION_SETDEFAULT:
+				fk_trigger->deferrable = false;
+				fk_trigger->initdeferred = false;
+				fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
+				break;
+			default:
+				elog(ERROR, "unrecognized FK action type: %d",
+					 (int) fkconstraint->fk_upd_action);
+				break;
+		}
 	}
 	fk_trigger->args = NIL;
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 2d9a8e9d54..2ff3d8b81b 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -776,6 +776,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 											  true, /* islocal */
 											  0,	/* inhcount */
 											  true, /* noinherit */
+											  false, /* contemporal */
 											  isInternal);	/* is_internal */
 	}
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 89887b8fd7..04f92fee95 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3174,6 +3174,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 							  true, /* is local */
 							  0,	/* inhcount */
 							  false,	/* connoinherit */
+							  false,	/* contemporal */
 							  false);	/* is_internal */
 	if (constrAddr)
 		ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6414aded0e..15705b7653 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2921,12 +2921,15 @@ _copyConstraint(const Constraint *from)
 	COPY_NODE_FIELD(where_clause);
 	COPY_NODE_FIELD(pktable);
 	COPY_NODE_FIELD(fk_attrs);
+	COPY_NODE_FIELD(fk_period);
 	COPY_NODE_FIELD(pk_attrs);
+	COPY_NODE_FIELD(pk_period);
 	COPY_SCALAR_FIELD(fk_matchtype);
 	COPY_SCALAR_FIELD(fk_upd_action);
 	COPY_SCALAR_FIELD(fk_del_action);
 	COPY_NODE_FIELD(old_conpfeqop);
 	COPY_SCALAR_FIELD(old_pktable_oid);
+	COPY_NODE_FIELD(without_overlaps);
 	COPY_SCALAR_FIELD(skip_validation);
 	COPY_SCALAR_FIELD(initially_valid);
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index a9b2f8bacd..33b7b0866c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1962,7 +1962,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 
 	index = makeNode(IndexStmt);
 
-	index->unique = (constraint->contype != CONSTR_EXCLUSION);
+	index->unique = (constraint->contype != CONSTR_EXCLUSION && constraint->without_overlaps == NULL);
 	index->primary = (constraint->contype == CONSTR_PRIMARY);
 	if (index->primary)
 	{
@@ -2351,6 +2351,148 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 				notnullcmd->name = pstrdup(key);
 				notnullcmds = lappend(notnullcmds, notnullcmd);
 			}
+
+			if (constraint->without_overlaps != NULL)
+			{
+				/*
+				 * We are building the index like for an EXCLUSION constraint,
+				 * so use the equality operator for these elements.
+				 */
+				List *opname = list_make1(makeString("="));
+				index->excludeOpNames = lappend(index->excludeOpNames, opname);
+			}
+		}
+
+		/*
+		 * Anything in without_overlaps should be included,
+		 * but with the overlaps operator (&&) instead of equality.
+		 */
+		if (constraint->without_overlaps != NULL)
+		{
+			char *without_overlaps_str = strVal(constraint->without_overlaps);
+			IndexElem *iparam = makeNode(IndexElem);
+
+			/*
+			 * Iterate through the table's columns
+			 * (like just a little bit above).
+			 * If we find one whose name is the same as without_overlaps,
+			 * validate that it's a range type.
+			 *
+			 * Otherwise iterate through the table's non-system PERIODs,
+			 * and if we find one then use its start/end columns
+			 * to construct a range expression.
+			 *
+			 * Otherwise report an error.
+			 */
+			bool       found = false;
+			ColumnDef  *column = NULL;
+			ListCell   *columns;
+
+			if (cxt->isalter)
+			{
+				// TODO: DRY this up with the non-ALTER case:
+				Relation rel = cxt->rel;
+				/*
+				 * Look up columns on existing table.
+				 */
+				for (int i = 0; i < rel->rd_att->natts; i++)
+				{
+					Form_pg_attribute attr = TupleDescAttr(rel->rd_att, i);
+					const char *attname = NameStr(attr->attname);
+					if (strcmp(attname, without_overlaps_str) == 0)
+					{
+						if (type_is_range(attr->atttypid))
+						{
+							found = true;
+							break;
+						}
+						else
+						{
+							ereport(ERROR,
+									(errcode(ERRCODE_DATATYPE_MISMATCH),
+									 errmsg("column \"%s\" named in WITHOUT OVERLAPS is not a range type",
+											without_overlaps_str)));
+						}
+					}
+				}
+			}
+			else
+			{
+				/*
+				 * Look up columns on the being-created table.
+				 */
+				foreach(columns, cxt->columns)
+				{
+					column = castNode(ColumnDef, lfirst(columns));
+					if (strcmp(column->colname, without_overlaps_str) == 0)
+					{
+						Oid colTypeOid = typenameTypeId(NULL, column->typeName);
+						if (type_is_range(colTypeOid))
+						{
+							found = true;
+							break;
+						}
+						else
+						{
+							ereport(ERROR,
+									(errcode(ERRCODE_DATATYPE_MISMATCH),
+									 errmsg("column \"%s\" named in WITHOUT OVERLAPS is not a range type",
+											without_overlaps_str)));
+						}
+					}
+				}
+			}
+			if (found)
+			{
+				AlterTableCmd *notnullcmd;
+				iparam->name = pstrdup(without_overlaps_str);	
+				iparam->expr = NULL;
+
+				/*
+				 * Force the column to NOT NULL since it is part of the primary key.
+				 */
+				notnullcmd = makeNode(AlterTableCmd);
+
+				notnullcmd->subtype = AT_SetNotNull;
+				notnullcmd->name = pstrdup(without_overlaps_str);
+				notnullcmds = lappend(notnullcmds, notnullcmd);
+			}
+			else 
+			{
+				found = false;
+				/*
+				 * TODO: Search for a non-system PERIOD with the right name.
+				 */
+				if (found)
+				{
+					iparam->name = NULL;
+					/*
+					 * TODO: Build up a parse tree to cast the period to a range.
+					 * See transformExpr (called below and defined in parser/parse_expr.c.
+					 */
+				}
+				else
+				{
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_COLUMN),
+							 errmsg("range or PERIOD \"%s\" named in WITHOUT OVERLAPS does not exist",
+									without_overlaps_str)));
+				}
+			}
+			{
+				List *opname;
+				iparam->indexcolname = NULL;
+				iparam->collation = NIL;
+				iparam->opclass = NIL;
+				iparam->ordering = SORTBY_DEFAULT;
+				iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+				index->indexParams = lappend(index->indexParams, iparam);
+
+				opname = list_make1(makeString("&&"));
+				index->excludeOpNames = lappend(index->excludeOpNames, opname);
+				index->accessMethod = "gist";
+				constraint->access_method = "gist";
+			}
 		}
 	}
 
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 8c895459c3..9cf3c99d78 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -109,6 +109,7 @@ typedef struct RI_ConstraintInfo
 	char		confupdtype;	/* foreign key's ON UPDATE action */
 	char		confdeltype;	/* foreign key's ON DELETE action */
 	char		confmatchtype;	/* foreign key's match type */
+	bool		temporal;		/* if the foreign key is temporal */
 	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 */
@@ -192,7 +193,7 @@ static int	ri_NullCheck(TupleDesc tupdesc, TupleTableSlot *slot,
 static void ri_BuildQueryKey(RI_QueryKey *key,
 							 const RI_ConstraintInfo *riinfo,
 							 int32 constr_queryno);
-static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
+static bool ri_KeysStable(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
 						 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
 							   Datum oldvalue, Datum newvalue);
@@ -353,18 +354,46 @@ RI_FKey_check(TriggerData *trigdata)
 
 		/* ----------
 		 * The query string built is
-		 *	SELECT 1 FROM [ONLY] <pktable> x WHERE pkatt1 = $1 [AND ...]
-		 *		   FOR KEY SHARE OF x
+		 *	SELECT 1
+		 *	FROM [ONLY] <pktable> x WHERE pkatt1 = $1 [AND ...]
+		 *	FOR KEY SHARE OF x
 		 * The type id's for the $ parameters are those of the
 		 * corresponding FK attributes.
+		 *
+		 * But for temporal FKs we need to make sure
+		 * the FK's range is completely covered.
+		 * So we use this query instead:
+		 *  SELECT 1
+		 *  FROM (
+		 *	    SELECT	range_agg(r, true, true) AS r
+		 *	    FROM	(
+		 *			SELECT pkperiodatt AS r
+		 *			FROM   [ONLY] pktable x
+		 *			WHERE  pkatt1 = $1 [AND ...]
+		 *			FOR KEY SHARE OF x
+		 *		) x1
+		 *  ) x2
+		 *  WHERE $n <@ x2.r[1]
+		 * Note if FOR KEY SHARE ever allows aggregate functions
+		 * we can make this a bit simpler.
 		 * ----------
 		 */
 		initStringInfo(&querybuf);
 		pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
 			"" : "ONLY ";
 		quoteRelationName(pkrelname, pk_rel);
-		appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
-						 pk_only, pkrelname);
+		if (riinfo->temporal)
+		{
+			quoteOneName(attname,
+					RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
+			appendStringInfo(&querybuf,
+					"SELECT 1 FROM (SELECT range_agg(r, true, true) AS r FROM (SELECT %s AS r FROM %s%s x",
+					attname, pk_only, pkrelname);
+		}
+		else {
+			appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
+							 pk_only, pkrelname);
+		}
 		querysep = "WHERE";
 		for (int i = 0; i < riinfo->nkeys; i++)
 		{
@@ -382,6 +411,8 @@ RI_FKey_check(TriggerData *trigdata)
 			queryoids[i] = fk_type;
 		}
 		appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
+		if (riinfo->temporal)
+			appendStringInfo(&querybuf, ") x1) x2 WHERE $%d <@ x2.r[1]", riinfo->nkeys);
 
 		/* Prepare and save the plan */
 		qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
@@ -1176,7 +1207,7 @@ RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
 		return false;
 
 	/* If all old and new key values are equal, no check is needed */
-	if (newslot && ri_KeysEqual(pk_rel, oldslot, newslot, riinfo, true))
+	if (newslot && ri_KeysStable(pk_rel, oldslot, newslot, riinfo, true))
 		return false;
 
 	/* Else we need to fire the trigger. */
@@ -1269,13 +1300,135 @@ RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
 		return true;
 
 	/* If all old and new key values are equal, no check is needed */
-	if (ri_KeysEqual(fk_rel, oldslot, newslot, riinfo, false))
+	if (ri_KeysStable(fk_rel, oldslot, newslot, riinfo, false))
 		return false;
 
 	/* Else we need to fire the trigger. */
 	return true;
 }
 
+/* ----------
+ * TRI_FKey_check_ins -
+ *
+ *	Check temporal foreign key existence at insert event on FK table.
+ * ----------
+ */
+Datum
+TRI_FKey_check_ins(PG_FUNCTION_ARGS)
+{
+	/*
+	 * Check that this is a valid trigger call on the right time and event.
+	 */
+	ri_CheckTrigger(fcinfo, "RI_FKey_check_ins", RI_TRIGTYPE_INSERT);
+
+	/*
+	 * Share code with UPDATE case.
+	 */
+	return RI_FKey_check((TriggerData *) fcinfo->context);
+}
+
+
+/* ----------
+ * TRI_FKey_check_upd -
+ *
+ *	Check temporal foreign key existence at update event on FK table.
+ * ----------
+ */
+Datum
+TRI_FKey_check_upd(PG_FUNCTION_ARGS)
+{
+	/*
+	 * Check that this is a valid trigger call on the right time and event.
+	 */
+	ri_CheckTrigger(fcinfo, "RI_FKey_check_upd", RI_TRIGTYPE_UPDATE);
+
+	/*
+	 * Share code with INSERT case.
+	 */
+	return RI_FKey_check((TriggerData *) fcinfo->context);
+}
+
+
+/* ----------
+ * TRI_FKey_noaction_del -
+ *
+ *	Give an error and roll back the current transaction if the
+ *	delete has resulted in a violation of the given temporal
+ *	referential integrity constraint.
+ * ----------
+ */
+Datum
+TRI_FKey_noaction_del(PG_FUNCTION_ARGS)
+{
+	/*
+	 * Check that this is a valid trigger call on the right time and event.
+	 */
+	ri_CheckTrigger(fcinfo, "TRI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
+
+	/*
+	 * Share code with RESTRICT/UPDATE cases.
+	 */
+	return ri_restrict((TriggerData *) fcinfo->context, true);
+}
+
+/*
+ * TRI_FKey_restrict_del -
+ *
+ * Restrict delete from PK table to rows unreferenced by foreign key.
+ *
+ * The SQL standard intends that this referential action occur exactly when
+ * the delete is performed, rather than after.  This appears to be
+ * the only difference between "NO ACTION" and "RESTRICT".  In Postgres
+ * we still implement this as an AFTER trigger, but it's non-deferrable.
+ */
+Datum
+TRI_FKey_restrict_del(PG_FUNCTION_ARGS)
+{
+	/* Check that this is a valid trigger call on the right time and event. */
+	ri_CheckTrigger(fcinfo, "TRI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
+
+	/* Share code with NO ACTION/UPDATE cases. */
+	return ri_restrict((TriggerData *) fcinfo->context, false);
+}
+
+/*
+ * TRI_FKey_noaction_upd -
+ *
+ * Give an error and roll back the current transaction if the
+ * update has resulted in a violation of the given referential
+ * integrity constraint.
+ */
+Datum
+TRI_FKey_noaction_upd(PG_FUNCTION_ARGS)
+{
+	/* Check that this is a valid trigger call on the right time and event. */
+	ri_CheckTrigger(fcinfo, "TRI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
+
+	/* Share code with RESTRICT/DELETE cases. */
+	return ri_restrict((TriggerData *) fcinfo->context, true);
+}
+
+/*
+ * TRI_FKey_restrict_upd -
+ *
+ * Restrict update of PK to rows unreferenced by foreign key.
+ *
+ * The SQL standard intends that this referential action occur exactly when
+ * the update is performed, rather than after.  This appears to be
+ * the only difference between "NO ACTION" and "RESTRICT".  In Postgres
+ * we still implement this as an AFTER trigger, but it's non-deferrable.
+ */
+Datum
+TRI_FKey_restrict_upd(PG_FUNCTION_ARGS)
+{
+	/* Check that this is a valid trigger call on the right time and event. */
+	ri_CheckTrigger(fcinfo, "TRI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
+
+	/* Share code with NO ACTION/DELETE cases. */
+	return ri_restrict((TriggerData *) fcinfo->context, false);
+}
+
+
 /*
  * RI_Initial_Check -
  *
@@ -2051,6 +2204,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
 	riinfo->confupdtype = conForm->confupdtype;
 	riinfo->confdeltype = conForm->confdeltype;
 	riinfo->confmatchtype = conForm->confmatchtype;
+	riinfo->temporal = conForm->contemporal;
 
 	DeconstructFkConstraintRow(tup,
 							   &riinfo->nkeys,
@@ -2649,9 +2803,12 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
 
 
 /*
- * ri_KeysEqual -
+ * ri_KeysStable -
  *
- * Check if all key values in OLD and NEW are equal.
+ * Check if all key values in OLD and NEW are "equivalent":
+ * For normal FKs we check for equality.
+ * For temporal FKs we check that the PK side is a superset of its old value,
+ * or the FK side is a subset.
  *
  * Note: at some point we might wish to redefine this as checking for
  * "IS NOT DISTINCT" rather than "=", that is, allow two nulls to be
@@ -2659,7 +2816,7 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
  * previously found at least one of the rows to contain no nulls.
  */
 static bool
-ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
+ri_KeysStable(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
 			 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
 	const int16 *attnums;
@@ -2692,29 +2849,43 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
 
 		if (rel_is_pk)
 		{
-			/*
-			 * If we are looking at the PK table, then do a bytewise
-			 * comparison.  We must propagate PK changes if the value is
-			 * changed to one that "looks" different but would compare as
-			 * equal using the equality operator.  This only makes a
-			 * difference for ON UPDATE CASCADE, but for consistency we treat
-			 * all changes to the PK the same.
-			 */
-			Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
+			if (riinfo->temporal)
+			{
+				return DatumGetBool(DirectFunctionCall2(range_contains, newvalue, oldvalue));
+			}
+			else
+			{
+				/*
+				 * If we are looking at the PK table, then do a bytewise
+				 * comparison.  We must propagate PK changes if the value is
+				 * changed to one that "looks" different but would compare as
+				 * equal using the equality operator.  This only makes a
+				 * difference for ON UPDATE CASCADE, but for consistency we treat
+				 * all changes to the PK the same.
+				 */
+				Form_pg_attribute att = TupleDescAttr(oldslot->tts_tupleDescriptor, attnums[i] - 1);
 
-			if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen))
-				return false;
+				if (!datum_image_eq(oldvalue, newvalue, att->attbyval, att->attlen))
+					return false;
+			}
 		}
 		else
 		{
-			/*
-			 * For the FK table, compare with the appropriate equality
-			 * operator.  Changes that compare equal will still satisfy the
-			 * constraint after the update.
-			 */
-			if (!ri_AttributesEqual(riinfo->ff_eq_oprs[i], RIAttType(rel, attnums[i]),
-									oldvalue, newvalue))
-				return false;
+			if (riinfo->temporal)
+			{
+				return DatumGetBool(DirectFunctionCall2(range_contains, oldvalue, newvalue));
+			}
+			else
+			{
+				/*
+				 * For the FK table, compare with the appropriate equality
+				 * operator.  Changes that compare equal will still satisfy the
+				 * constraint after the update.
+				 */
+				if (!ri_AttributesEqual(riinfo->ff_eq_oprs[i], RIAttType(rel, attnums[i]),
+										oldvalue, newvalue))
+					return false;
+			}
 		}
 	}
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0c58f1f109..473b0d1aba 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -317,7 +317,7 @@ static char *pg_get_viewdef_worker(Oid viewoid,
 								   int prettyFlags, int wrapColumn);
 static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
 static int	decompile_column_index_array(Datum column_index_array, Oid relId,
-										 StringInfo buf);
+										 bool withoutOverlaps, bool withPeriod, StringInfo buf);
 static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
 static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
 									const Oid *excludeOps,
@@ -1985,6 +1985,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 			{
 				Datum		val;
 				bool		isnull;
+				bool		hasperiod;
 				const char *string;
 
 				/* Start off the constraint definition */
@@ -1997,7 +1998,13 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					elog(ERROR, "null conkey for constraint %u",
 						 constraintId);
 
-				decompile_column_index_array(val, conForm->conrelid, &buf);
+				/*
+				 * If it is a temporal foreign key
+				 * then it uses PERIOD.
+				 */
+				hasperiod = DatumGetBool(SysCacheGetAttr(CONSTROID, tup,
+						  Anum_pg_constraint_contemporal, &isnull));
+				decompile_column_index_array(val, conForm->conrelid, false, hasperiod, &buf);
 
 				/* add foreign relation name */
 				appendStringInfo(&buf, ") REFERENCES %s(",
@@ -2011,7 +2018,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					elog(ERROR, "null confkey for constraint %u",
 						 constraintId);
 
-				decompile_column_index_array(val, conForm->confrelid, &buf);
+				decompile_column_index_array(val, conForm->confrelid, false, hasperiod, &buf);
 
 				appendStringInfoChar(&buf, ')');
 
@@ -2112,7 +2119,13 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 					elog(ERROR, "null conkey for constraint %u",
 						 constraintId);
 
-				keyatts = decompile_column_index_array(val, conForm->conrelid, &buf);
+				/*
+				 * If it has exclusion-style operator OIDs
+				 * then it uses WITHOUT OVERLAPS.
+				 */
+				SysCacheGetAttr(CONSTROID, tup,
+						  Anum_pg_constraint_conexclop, &isnull);
+				keyatts = decompile_column_index_array(val, conForm->conrelid, !isnull, false, &buf);
 
 				appendStringInfoChar(&buf, ')');
 
@@ -2314,7 +2327,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
  */
 static int
 decompile_column_index_array(Datum column_index_array, Oid relId,
-							 StringInfo buf)
+							 bool withoutOverlaps, bool withPeriod, StringInfo buf)
 {
 	Datum	   *keys;
 	int			nKeys;
@@ -2332,9 +2345,21 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
 		colName = get_attname(relId, DatumGetInt16(keys[j]), false);
 
 		if (j == 0)
+		{
 			appendStringInfoString(buf, quote_identifier(colName));
+		}
+		else if (withoutOverlaps && j == nKeys - 1)
+		{
+			appendStringInfo(buf, ", %s WITHOUT OVERLAPS", quote_identifier(colName));
+		}
+		else if (withPeriod && j == nKeys - 1)
+		{
+			appendStringInfo(buf, ", PERIOD %s", quote_identifier(colName));
+		}
 		else
+		{
 			appendStringInfo(buf, ", %s", quote_identifier(colName));
+		}
 	}
 
 	return nKeys;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7aa5d7c7fa..4d863d0715 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4949,8 +4949,9 @@ restart:
  * RelationGetExclusionInfo -- get info about index's exclusion constraint
  *
  * This should be called only for an index that is known to have an
- * associated exclusion constraint.  It returns arrays (palloc'd in caller's
- * context) of the exclusion operator OIDs, their underlying functions'
+ * associated exclusion constraint or temporal primary key.
+ * It returns arrays (palloc'd in caller's * context)
+ * of the exclusion operator OIDs, their underlying functions'
  * OIDs, and their strategy numbers in the index's opclasses.  We cache
  * all this information since it requires a fair amount of work to get.
  */
@@ -5016,7 +5017,12 @@ RelationGetExclusionInfo(Relation indexRelation,
 		int			nelem;
 
 		/* We want the exclusion constraint owning the index */
-		if (conform->contype != CONSTRAINT_EXCLUSION ||
+		/*
+		 * TODO: Is this too permissive?
+		 * Maybe it needs to be (!= CONSTRAINT_PRIMARY || !has_excl_operators)
+		 */
+		if ((conform->contype != CONSTRAINT_EXCLUSION &&
+					conform->contype != CONSTRAINT_PRIMARY) ||
 			conform->conindid != RelationGetRelid(indexRelation))
 			continue;
 
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index c1e60c7dfd..0baa22c0e5 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -103,6 +103,12 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
 	/* Has a local definition and cannot be inherited */
 	bool		connoinherit;
 
+	/*
+	 * For primary and foreign keys, signifies the last column is a range 
+	 * and should use overlaps instead of equals.
+	 */
+	bool		contemporal;
+
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 
 	/*
@@ -117,26 +123,26 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
 	int16		confkey[1];
 
 	/*
-	 * If a foreign key, the OIDs of the PK = FK equality operators for each
+	 * If a foreign key, the OIDs of the PK = FK comparison operators for each
 	 * column of the constraint
 	 */
 	Oid			conpfeqop[1];
 
 	/*
-	 * If a foreign key, the OIDs of the PK = PK equality operators for each
+	 * If a foreign key, the OIDs of the PK = PK comparison 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
+	 * If a foreign key, the OIDs of the FK = FK comparison operators for each
 	 * column of the constraint (i.e., equality for the referencing columns)
 	 */
 	Oid			conffeqop[1];
 
 	/*
 	 * If an exclusion constraint, the OIDs of the exclusion operators for
-	 * each column of the constraint
+	 * each column of the constraint. Also set for temporal primary keys.
 	 */
 	Oid			conexclop[1];
 
@@ -211,6 +217,7 @@ extern Oid	CreateConstraintEntry(const char *constraintName,
 								  bool conIsLocal,
 								  int conInhCount,
 								  bool conNoInherit,
+								  bool conTemporal,
 								  bool is_internal);
 
 extern void RemoveConstraintById(Oid conId);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0902dce5f1..1bca4e417e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -3703,6 +3703,50 @@
   prorettype => 'trigger', proargtypes => '',
   prosrc => 'RI_FKey_noaction_upd' },
 
+# Temporal referential integrity constraint triggers
+{ oid => '6122', descr => 'temporal referential integrity FOREIGN KEY ... REFERENCES',
+  proname => 'TRI_FKey_check_ins', provolatile => 'v', prorettype => 'trigger',
+  proargtypes => '', prosrc => 'TRI_FKey_check_ins' },
+{ oid => '6123', descr => 'temporal referential integrity FOREIGN KEY ... REFERENCES',
+  proname => 'TRI_FKey_check_upd', provolatile => 'v', prorettype => 'trigger',
+  proargtypes => '', prosrc => 'TRI_FKey_check_upd' },
+# { oid => '6124', descr => 'temporal referential integrity ON DELETE CASCADE',
+#   proname => 'TRI_FKey_cascade_del', provolatile => 'v', prorettype => 'trigger',
+#   proargtypes => '', prosrc => 'TRI_FKey_cascade_del' },
+# { oid => '6125', descr => 'temporal referential integrity ON UPDATE CASCADE',
+#   proname => 'TRI_FKey_cascade_upd', provolatile => 'v', prorettype => 'trigger',
+#   proargtypes => '', prosrc => 'TRI_FKey_cascade_upd' },
+{ oid => '6126', descr => 'temporal referential integrity ON DELETE RESTRICT',
+  proname => 'TRI_FKey_restrict_del', provolatile => 'v',
+  prorettype => 'trigger', proargtypes => '',
+  prosrc => 'TRI_FKey_restrict_del' },
+{ oid => '6127', descr => 'temporal referential integrity ON UPDATE RESTRICT',
+  proname => 'TRI_FKey_restrict_upd', provolatile => 'v',
+  prorettype => 'trigger', proargtypes => '',
+  prosrc => 'TRI_FKey_restrict_upd' },
+# { oid => '6128', descr => 'temporal referential integrity ON DELETE SET NULL',
+#   proname => 'TRI_FKey_setnull_del', provolatile => 'v', prorettype => 'trigger',
+#   proargtypes => '', prosrc => 'TRI_FKey_setnull_del' },
+# { oid => '6129', descr => 'temporal referential integrity ON UPDATE SET NULL',
+#   proname => 'TRI_FKey_setnull_upd', provolatile => 'v', prorettype => 'trigger',
+#   proargtypes => '', prosrc => 'TRI_FKey_setnull_upd' },
+# { oid => '6130', descr => 'temporal referential integrity ON DELETE SET DEFAULT',
+#   proname => 'TRI_FKey_setdefault_del', provolatile => 'v',
+#   prorettype => 'trigger', proargtypes => '',
+#   prosrc => 'TRI_FKey_setdefault_del' },
+# { oid => '6131', descr => 'temporal referential integrity ON UPDATE SET DEFAULT',
+#   proname => 'TRI_FKey_setdefault_upd', provolatile => 'v',
+#   prorettype => 'trigger', proargtypes => '',
+#   prosrc => 'TRI_FKey_setdefault_upd' },
+{ oid => '6132', descr => 'temporal referential integrity ON DELETE NO ACTION',
+  proname => 'TRI_FKey_noaction_del', provolatile => 'v',
+  prorettype => 'trigger', proargtypes => '',
+  prosrc => 'TRI_FKey_noaction_del' },
+{ oid => '6133', descr => 'temporal referential integrity ON UPDATE NO ACTION',
+  proname => 'TRI_FKey_noaction_upd', provolatile => 'v',
+  prorettype => 'trigger', proargtypes => '',
+  prosrc => 'TRI_FKey_noaction_upd' },
+
 { oid => '1666',
   proname => 'varbiteq', proleakproof => 't', prorettype => 'bool',
   proargtypes => 'varbit varbit', prosrc => 'biteq' },
