0001-fixup-Add-temporal-PRIMARY-KEY-and-UNIQUE-constraint.patch.nocfbot

text/plain

Filename: 0001-fixup-Add-temporal-PRIMARY-KEY-and-UNIQUE-constraint.patch.nocfbot
Type: text/plain
Part: 0
Message: Re: SQL:2011 application time
From 6d7af1f78505c08fb205a56bd1ba3cb951f04002 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 9 Nov 2023 14:11:34 +0100
Subject: [PATCH] fixup! Add temporal PRIMARY KEY and UNIQUE constraints

---
 doc/src/sgml/catalogs.sgml                    |  8 +--
 doc/src/sgml/ref/create_table.sgml            | 68 ++++++++-----------
 src/backend/catalog/heap.c                    |  4 +-
 src/backend/catalog/index.c                   | 14 ++--
 src/backend/catalog/pg_constraint.c           |  4 +-
 src/backend/commands/indexcmds.c              | 30 ++++----
 src/backend/commands/tablecmds.c              | 16 ++---
 src/backend/commands/trigger.c                |  2 +-
 src/backend/commands/typecmds.c               |  2 +-
 src/backend/parser/gram.y                     | 19 +++---
 src/backend/parser/parse_utilcmd.c            | 28 ++++++--
 src/backend/utils/adt/ruleutils.c             | 27 +++-----
 src/backend/utils/cache/relcache.c            | 16 +++--
 src/bin/pg_dump/pg_dump.c                     | 33 +++------
 src/bin/pg_dump/pg_dump.h                     |  2 +-
 src/bin/pg_dump/t/002_pg_dump.pl              | 14 ++--
 src/bin/psql/describe.c                       | 10 +--
 src/include/catalog/index.h                   |  2 +-
 src/include/catalog/pg_constraint.h           | 11 +--
 src/include/commands/defrem.h                 |  2 +-
 src/include/nodes/parsenodes.h                |  6 +-
 .../regress/expected/without_overlaps.out     |  8 +--
 src/test/regress/parallel_schedule            |  4 +-
 23 files changed, 148 insertions(+), 182 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 5412f2daa0..618a05ac89 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2711,13 +2711,11 @@ <title><structname>pg_constraint</structname> Columns</title>
 
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
-       <structfield>contemporal</structfield> <type>bool</type>
+       <structfield>conwithoutoverlaps</structfield> <type>bool</type>
       </para>
       <para>
-       This constraint is a application-time temporal constraint,
-       defined with <literal>WITHOUT OVERLAPS</literal> (for primary
-       key and unique constraints) or <literal>PERIOD</literal> (for
-       foreign keys).
+       This constraint is defined with <literal>WITHOUT OVERLAPS</literal>
+       (for primary keys and unique constraints).
       </para></entry>
      </row>
 
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index ad5bf21453..a0bd2b8adc 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -78,8 +78,8 @@
 [ CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> ]
 { CHECK ( <replaceable class="parameter">expression</replaceable> ) [ NO INHERIT ] |
   NOT NULL <replaceable class="parameter">column_name</replaceable> [ NO INHERIT ] |
-  UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">temporal_interval</replaceable> WITHOUT OVERLAPS ] ) <replaceable class="parameter">index_parameters</replaceable> |
-  PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">temporal_interval</replaceable> WITHOUT OVERLAPS ] ) <replaceable class="parameter">index_parameters</replaceable> |
+  UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] ) <replaceable class="parameter">index_parameters</replaceable> |
+  PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] ) <replaceable class="parameter">index_parameters</replaceable> |
   EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
   FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
     [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable
@@ -107,10 +107,6 @@
 
 { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]
 
-<phrase><replaceable class="parameter">temporal_interval</replaceable> in a <literal>PRIMARY KEY</literal>, <literal>UNIQUE</literal>, or <literal>FOREIGN KEY</literal> constraint is:</phrase>
-
-<replaceable class="parameter">range_column_name</replaceable>
-
 <phrase><replaceable class="parameter">referential_action</replaceable> in a <literal>FOREIGN KEY</literal>/<literal>REFERENCES</literal> constraint is:</phrase>
 
 { NO ACTION | RESTRICT | CASCADE | SET NULL [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] | SET DEFAULT [ ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) ] }
@@ -969,7 +965,7 @@ <title>Parameters</title>
 
    <varlistentry id="sql-createtable-parms-unique">
     <term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ]</literal> (column constraint)</term>
-    <term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] )</literal>
+    <term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] )</literal>
     <optional> <literal>INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...])</literal> </optional> (table constraint)</term>
 
     <listitem>
@@ -983,6 +979,26 @@ <title>Parameters</title>
       of these columns.
      </para>
 
+     <para>
+      If the <literal>WITHOUT OVERLAPS</literal> option is specified for the
+      last column, then that column is checked for overlaps instead of
+      equality.  In that case, the other columns of the constraint will allow
+      duplicates so long as the duplicates don't overlap in the
+      <literal>WITHOUT OVERLAPS</literal> column.  (This is sometimes called a
+      temporal key, if the column is a range of dates or timestamps, but
+      PostgreSQL allows ranges over any base type.)  In effect, such a
+      constraint is enforced with an <literal>EXCLUDE</literal> constraint
+      rather than a <literal>UNIQUE</literal> constraint.  So for example
+      <literal>UNIQUE (id, valid_at WITHOUT OVERLAPS)</literal> behaves like
+      <literal>EXCLUDE USING GIST (id WITH =, valid_at WITH
+      &amp;&amp;)</literal>.  The <literal>WITHOUT OVERLAPS</literal> column
+      must have a range type.  The non-<literal>WITHOUT OVERLAPS</literal>
+      columns of the constraint can be any type that can be compared for
+      equality in a GiST index. By default only range types are supported, but
+      you can use other types by adding the <xref linkend="btree-gist"/>
+      extension (which is the expected way to use this feature).
+     </para>
+
      <para>
       For the purpose of a unique constraint, null values are not
       considered equal, unless <literal>NULLS NOT DISTINCT</literal> is
@@ -1007,8 +1023,7 @@ <title>Parameters</title>
       Adding a unique constraint will automatically create a unique btree
       index on the column or group of columns used in the constraint. But if
       the constraint includes a <literal>WITHOUT OVERLAPS</literal> clause,
-      it will use a GiST index and behave like a temporal <literal>PRIMARY
-      KEY</literal>: preventing duplicates only in overlapping time periods.
+      it will use a GiST index.
      </para>
 
      <para>
@@ -1026,8 +1041,7 @@ <title>Parameters</title>
 
    <varlistentry id="sql-createtable-parms-primary-key">
     <term><literal>PRIMARY KEY</literal> (column constraint)</term>
-    <term><literal>PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ]
-    [, <replaceable class="parameter">temporal_interval</replaceable> WITHOUT OVERLAPS ] )</literal>
+    <term><literal>PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] )</literal>
     <optional> <literal>INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...])</literal> </optional> (table constraint)</term>
     <listitem>
      <para>
@@ -1060,9 +1074,10 @@ <title>Parameters</title>
      </para>
 
      <para>
-      Adding a <literal>PRIMARY KEY</literal> constraint will automatically
-      create a unique btree index (or GiST if temporal) on the column or group of
-      columns used in the constraint.
+      As with a <literal>UNIQUE</literal> constraint, adding a
+      <literal>PRIMARY KEY</literal> constraint will automatically create a
+      unique btree index, or GiST if <literal>WITHOUT OVERLAPS</literal> was
+      specified, on the column or group of columns used in the constraint.
      </para>
 
      <para>
@@ -1075,31 +1090,6 @@ <title>Parameters</title>
       (e.g., <literal>DROP COLUMN</literal>) can cause cascaded constraint and
       index deletion.
      </para>
-
-     <para>
-      A <literal>PRIMARY KEY</literal> with a <literal>WITHOUT OVERLAPS</literal> option
-      is a <emphasis>temporal</emphasis> primary key.
-      The <literal>WITHOUT OVERLAPS</literal> column
-      must be a range type or a <literal>PERIOD</literal>
-      and is used to constrain the record's applicability
-      to just that range/period (usually a range of dates or timestamps,
-      although Postgres allows a range/period over any base type).
-     </para>
-
-     <para>
-      The non-<literal>WITHOUT OVERLAPS</literal> part(s) of the key can be any
-      type that can be compared for equality in a GiST index. By default
-      only range types are supported, but you can use other types by
-      adding the <xref linkend="btree-gist"/> extension
-      (which is the expected way to use this feature).
-      These ordinary parts of the primary key do allow duplicates,
-      so long as the duplicates don't overlap in the
-      <literal>WITHOUT OVERLAPS</literal> column.
-      In effect a temporal <literal>PRIMARY KEY</literal> is enforced with an
-      <literal>EXCLUDE</literal> constraint rather than a <literal>UNIQUE</literal>
-      constraint. So for example <literal>PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)</literal>
-      behaves like <literal>EXCLUDE USING GIST (id WITH =, valid_at WITH &amp;&amp;)</literal>.
-     </para>
     </listitem>
    </varlistentry>
 
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3e9cc4bb10..f126d8e676 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2140,7 +2140,7 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
 							  is_local, /* conislocal */
 							  inhcount, /* coninhcount */
 							  is_no_inherit,	/* connoinherit */
-							  false,	/* contemporal */
+							  false,	/* conwithoutoverlaps */
 							  is_internal); /* internally constructed? */
 
 	pfree(ccbin);
@@ -2191,7 +2191,7 @@ StoreRelNotNull(Relation rel, const char *nnname, AttrNumber attnum,
 							  is_local,
 							  inhcount,
 							  is_no_inherit,
-							  false,	/* contemporal */
+							  false,	/* conwithoutoverlaps */
 							  false);
 	return constrOid;
 }
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 2f1ec3b0ac..8444d768e5 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -254,8 +254,8 @@ index_check_primary_key(Relation heapRel,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("primary keys cannot be expressions")));
 
-		/* System attributes are never null, so no need to check. */
-		if (attnum <= 0)
+		/* System attributes are never null, so no need to check */
+		if (attnum < 0)
 			continue;
 
 		atttuple = SearchSysCache2(ATTNUM,
@@ -1326,7 +1326,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId,
 											Anum_pg_index_indoption);
 	indcoloptions = (int2vector *) DatumGetPointer(colOptionDatum);
 
-	/* Fetch options of index if any */
+	/* Fetch reloptions of index if any */
 	classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(oldIndexId));
 	if (!HeapTupleIsValid(classTuple))
 		elog(ERROR, "cache lookup failed for relation %u", oldIndexId);
@@ -1895,7 +1895,7 @@ index_concurrently_set_dead(Oid heapId, Oid indexId)
  *		INDEX_CONSTR_CREATE_UPDATE_INDEX: update the pg_index row
  *		INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS: remove existing dependencies
  *			of index on table's columns
- *		INDEX_CONSTR_CREATE_TEMPORAL: constraint is for a temporal primary key
+ *		INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS: constraint uses WITHOUT OVERLAPS
  * allow_system_table_mods: allow table to be a system catalog
  * is_internal: index is constructed due to internal process
  */
@@ -1919,13 +1919,13 @@ index_constraint_create(Relation heapRelation,
 	bool		mark_as_primary;
 	bool		islocal;
 	bool		noinherit;
-	bool		is_temporal;
+	bool		is_without_overlaps;
 	int			inhcount;
 
 	deferrable = (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) != 0;
 	initdeferred = (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED) != 0;
 	mark_as_primary = (constr_flags & INDEX_CONSTR_CREATE_MARK_AS_PRIMARY) != 0;
-	is_temporal = (constr_flags & INDEX_CONSTR_CREATE_TEMPORAL) != 0;
+	is_without_overlaps = (constr_flags & INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS) != 0;
 
 	/* constraint creation support doesn't work while bootstrapping */
 	Assert(!IsBootstrapProcessingMode());
@@ -2002,7 +2002,7 @@ index_constraint_create(Relation heapRelation,
 								   islocal,
 								   inhcount,
 								   noinherit,
-								   is_temporal,	/* contemporal */
+								   is_without_overlaps,
 								   is_internal);
 
 	/*
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index d5329ca8c9..ffd340a2e4 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -78,7 +78,7 @@ CreateConstraintEntry(const char *constraintName,
 					  bool conIsLocal,
 					  int conInhCount,
 					  bool conNoInherit,
-					  bool conTemporal,
+					  bool conWithoutOverlaps,
 					  bool is_internal)
 {
 	Relation	conDesc;
@@ -194,7 +194,7 @@ CreateConstraintEntry(const char *constraintName,
 	values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
 	values[Anum_pg_constraint_coninhcount - 1] = Int16GetDatum(conInhCount);
 	values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
-	values[Anum_pg_constraint_contemporal - 1] = BoolGetDatum(conTemporal);
+	values[Anum_pg_constraint_conwithoutoverlaps - 1] = BoolGetDatum(conWithoutOverlaps);
 
 	if (conkeyArray)
 		values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index e9d1b17935..d56db81fa1 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -91,7 +91,7 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
 							  Oid accessMethodId,
 							  bool amcanorder,
 							  bool isconstraint,
-							  bool istemporal,
+							  bool iswithoutoverlaps,
 							  Oid ddl_userid,
 							  int ddl_sec_context,
 							  int *ddl_save_nestlevel);
@@ -149,7 +149,7 @@ typedef struct ReindexErrorInfo
  *		to index on.
  * 'exclusionOpNames': list of names of exclusion-constraint operators,
  *		or NIL if not an exclusion constraint.
- * 'istemporal': true iff this index has a WITHOUT OVERLAPS clause.
+ * 'isWithoutOverlaps': true iff this index has a WITHOUT OVERLAPS clause.
  *
  * This is tailored to the needs of ALTER TABLE ALTER TYPE, which recreates
  * any indexes that depended on a changing column from their pg_get_indexdef
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 					 const char *accessMethodName,
 					 const List *attributeList,
 					 const List *exclusionOpNames,
-					 bool istemporal)
+					 bool isWithoutOverlaps)
 {
 	bool		isconstraint;
 	Oid		   *typeIds;
@@ -255,7 +255,7 @@ CheckIndexCompatible(Oid oldId,
 					  coloptions, attributeList,
 					  exclusionOpNames, relationId,
 					  accessMethodName, accessMethodId,
-					  amcanorder, isconstraint, istemporal, InvalidOid,
+					  amcanorder, isconstraint, isWithoutOverlaps, InvalidOid,
 					  0, NULL);
 
 	/* Get the soon-obsolete pg_index tuple. */
@@ -689,7 +689,7 @@ DefineIndex(Oid tableId,
 	 * It has exclusion constraint behavior if it's an EXCLUDE constraint
 	 * or a temporal PRIMARY KEY/UNIQUE constraint
 	 */
-	exclusion = stmt->excludeOpNames || stmt->istemporal;
+	exclusion = stmt->excludeOpNames || stmt->iswithoutoverlaps;
 
 	/* Ensure that it makes sense to index this kind of relation */
 	switch (rel->rd_rel->relkind)
@@ -859,7 +859,7 @@ DefineIndex(Oid tableId,
 	pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
 								 accessMethodId);
 
-	if (stmt->unique && !stmt->istemporal && !amRoutine->amcanunique)
+	if (stmt->unique && !stmt->iswithoutoverlaps && !amRoutine->amcanunique)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("access method \"%s\" does not support unique indexes",
@@ -927,7 +927,7 @@ DefineIndex(Oid tableId,
 					  coloptions, allIndexParams,
 					  stmt->excludeOpNames, tableId,
 					  accessMethodName, accessMethodId,
-					  amcanorder, stmt->isconstraint, stmt->istemporal,
+					  amcanorder, stmt->isconstraint, stmt->iswithoutoverlaps,
 					  root_save_userid, root_save_sec_context,
 					  &root_save_nestlevel);
 
@@ -955,8 +955,6 @@ DefineIndex(Oid tableId,
 
 		if (stmt->primary)
 			constraint_type = "PRIMARY KEY";
-		else if (stmt->unique && stmt->istemporal)
-			constraint_type = "temporal UNIQUE";
 		else if (stmt->unique)
 			constraint_type = "UNIQUE";
 		else if (stmt->excludeOpNames)
@@ -1006,7 +1004,7 @@ DefineIndex(Oid tableId,
 			 * we have an exclusion constraint (or a temporal PK), it already
 			 * knows the operators, so we don't have to infer them.
 			 */
-			if (stmt->unique && !stmt->istemporal && accessMethodId != BTREE_AM_OID)
+			if (stmt->unique && !stmt->iswithoutoverlaps && accessMethodId != BTREE_AM_OID)
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot match partition key to an index using access method \"%s\"",
@@ -1039,7 +1037,7 @@ DefineIndex(Oid tableId,
 					{
 						Oid			idx_eqop = InvalidOid;
 
-						if (stmt->unique && !stmt->istemporal)
+						if (stmt->unique && !stmt->iswithoutoverlaps)
 							idx_eqop = get_opfamily_member(idx_opfamily,
 														   idx_opcintype,
 														   idx_opcintype,
@@ -1197,8 +1195,8 @@ DefineIndex(Oid tableId,
 		constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
 	if (stmt->initdeferred)
 		constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
-	if (stmt->istemporal)
-		constr_flags |= INDEX_CONSTR_CREATE_TEMPORAL;
+	if (stmt->iswithoutoverlaps)
+		constr_flags |= INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS;
 
 	indexRelationId =
 		index_create(rel, indexRelationName, indexRelationId, parentIndexId,
@@ -1975,7 +1973,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 				  Oid accessMethodId,
 				  bool amcanorder,
 				  bool isconstraint,
-				  bool istemporal,
+				  bool iswithoutoverlaps,
 				  Oid ddl_userid,
 				  int ddl_sec_context,
 				  int *ddl_save_nestlevel)
@@ -2000,7 +1998,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 		nextExclOp = NULL;
 
 	/* exclusionOpNames can be non-NIL if we are creating a partition */
-	if (istemporal && exclusionOpNames == NIL)
+	if (iswithoutoverlaps && exclusionOpNames == NIL)
 	{
 		indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
 		indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
@@ -2283,7 +2281,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 			indexInfo->ii_ExclusionStrats[attn] = strat;
 			nextExclOp = lnext(exclusionOpNames, nextExclOp);
 		}
-		else if (istemporal)
+		else if (iswithoutoverlaps)
 		{
 			int strat;
 			Oid opid;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4cc645282a..04527a3540 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -208,7 +208,6 @@ typedef struct NewConstraint
 	Oid			refrelid;		/* PK rel, if FOREIGN */
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
-	bool		contemporal;	/* Whether the new constraint is temporal */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
 	ExprState  *qualstate;		/* Execution state for CHECK expr */
 } NewConstraint;
@@ -10094,7 +10093,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 									  conislocal,	/* islocal */
 									  coninhcount,	/* inhcount */
 									  connoinherit, /* conNoInherit */
-									  false,	/* conTemporal */
+									  false,	/* conWithoutOverlaps */
 									  false);	/* is_internal */
 
 	ObjectAddressSet(address, ConstraintRelationId, constrOid);
@@ -10393,7 +10392,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 									  false,
 									  1,
 									  false,
-									  false,	/* conTemporal */
+									  false,	/* conWithoutOverlaps */
 									  false);
 
 			/*
@@ -10899,7 +10898,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 								  false,	/* islocal */
 								  1,	/* inhcount */
 								  false,	/* conNoInherit */
-								  false,	/* conTemporal */
+								  false,	/* conWithoutOverlaps */
 								  true);
 
 		/* Set up partition dependencies for the new constraint */
@@ -11574,7 +11573,6 @@ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
 			newcon->refrelid = con->confrelid;
 			newcon->refindid = con->conindid;
 			newcon->conid = con->oid;
-			newcon->contemporal = con->contemporal;
 			newcon->qual = (Node *) fkconstraint;
 
 			/* Find or create work queue entry for this table */
@@ -11741,12 +11739,10 @@ 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.  If this is a temporal primary key,
- *	also set the WITHOUT OVERLAPS attribute name, attnum, and atttypid.
+ *	index supporting the primary key.
  *
  *	All parameters except pkrel are output parameters.  Also, the function
- *	return value is the number of attributes in the primary key,
- *	not including the WITHOUT OVERLAPS if any.
+ *	return value is the number of attributes in the primary key.
  *
  *	Used when the column list in the REFERENCES specification is omitted.
  */
@@ -14292,7 +14288,7 @@ TryReuseIndex(Oid oldId, IndexStmt *stmt)
 							 stmt->accessMethod,
 							 stmt->indexParams,
 							 stmt->excludeOpNames,
-							 stmt->istemporal))
+							 stmt->iswithoutoverlaps))
 	{
 		Relation	irel = index_open(oldId, NoLock);
 
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 0577b60415..a10a42b97f 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -841,7 +841,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 											  true, /* islocal */
 											  0,	/* inhcount */
 											  true, /* noinherit */
-											  false, /* contemporal */
+											  false, /* conwithoutoverlaps */
 											  isInternal);	/* is_internal */
 	}
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 1773556053..2470019818 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3544,7 +3544,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 							  true, /* is local */
 							  0,	/* inhcount */
 							  false,	/* connoinherit */
-							  false,	/* contemporal */
+							  false,	/* conwithoutoverlaps */
 							  false);	/* is_internal */
 	if (constrAddr)
 		ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 14d4e24fcc..ff4add6225 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -523,13 +523,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <node>	TableElement TypedTableElement ConstraintElem TableFuncElement
 %type <node>	columnDef columnOptions
 %type <defelt>	def_elem reloption_elem old_aggr_elem operator_def_elem
-%type <node>	def_arg columnElem without_overlaps_clause
-				where_clause where_or_current_clause
+%type <node>	def_arg columnElem where_clause where_or_current_clause
 				a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
 				columnref in_expr having_clause func_table xmltable array_expr
 				OptWhereClause operator_def_arg
 %type <list>	rowsfrom_item rowsfrom_list opt_col_def_list
-%type <boolean> opt_ordinality
+%type <boolean> opt_ordinality opt_without_overlaps
 %type <list>	ExclusionConstraintList ExclusionConstraintElem
 %type <list>	func_arg_list func_arg_list_opt
 %type <node>	func_arg_expr
@@ -4105,7 +4104,7 @@ ConstraintElem:
 					n->initially_valid = true;
 					$$ = (Node *) n;
 				}
-			| UNIQUE opt_unique_null_treatment '(' columnList without_overlaps_clause ')' opt_c_include opt_definition OptConsTableSpace
+			| UNIQUE opt_unique_null_treatment '(' columnList opt_without_overlaps ')' opt_c_include opt_definition OptConsTableSpace
 				ConstraintAttributeSpec
 				{
 					Constraint *n = makeNode(Constraint);
@@ -4140,7 +4139,7 @@ ConstraintElem:
 								   NULL, yyscanner);
 					$$ = (Node *) n;
 				}
-			| PRIMARY KEY '(' columnList without_overlaps_clause ')' opt_c_include opt_definition OptConsTableSpace
+			| PRIMARY KEY '(' columnList opt_without_overlaps ')' opt_c_include opt_definition OptConsTableSpace
 				ConstraintAttributeSpec
 				{
 					Constraint *n = makeNode(Constraint);
@@ -4221,6 +4220,11 @@ opt_no_inherit:	NO INHERIT							{  $$ = true; }
 			| /* EMPTY */							{  $$ = false; }
 		;
 
+opt_without_overlaps:
+			WITHOUT OVERLAPS						{ $$ = true; }
+			| /*EMPTY*/								{ $$ = false; }
+	;
+
 opt_column_list:
 			'(' columnList ')'						{ $$ = $2; }
 			| /*EMPTY*/								{ $$ = NIL; }
@@ -4231,11 +4235,6 @@ columnList:
 			| columnList ',' columnElem				{ $$ = lappend($1, $3); }
 		;
 
-without_overlaps_clause:
-			',' columnElem WITHOUT OVERLAPS { $$ = $2; }
-			| /*EMPTY*/               { $$ = NULL; }
-	;
-
 columnElem: ColId
 				{
 					$$ = (Node *) makeString($1);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index f4705ecc2b..547e3058b9 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -124,7 +124,7 @@ static List *get_opclass(Oid opclass, Oid actual_datatype);
 static void transformIndexConstraints(CreateStmtContext *cxt);
 static IndexStmt *transformIndexConstraint(Constraint *constraint,
 										   CreateStmtContext *cxt);
-static void validateWithoutOverlaps(CreateStmtContext *cxt, char *colname, int location);
+static void validateWithoutOverlaps(CreateStmtContext *cxt, const char *colname, int location);
 static void transformExtendedStatistics(CreateStmtContext *cxt);
 static void transformFKConstraints(CreateStmtContext *cxt,
 								   bool skipValidation,
@@ -1717,7 +1717,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 	index->unique = idxrec->indisunique;
 	index->nulls_not_distinct = idxrec->indnullsnotdistinct;
 	index->primary = idxrec->indisprimary;
-	index->istemporal = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
+	index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
 	index->transformed = true;	/* don't need transformIndexStmt */
 	index->concurrent = false;
 	index->if_not_exists = false;
@@ -1768,7 +1768,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 				int			i;
 
 				Assert(conrec->contype == CONSTRAINT_EXCLUSION ||
-						(index->istemporal &&
+						(index->iswithoutoverlaps &&
 						 (conrec->contype == CONSTRAINT_PRIMARY || conrec->contype == CONSTRAINT_UNIQUE)));
 				/* Extract operator OIDs from the pg_constraint tuple */
 				datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr,
@@ -2309,7 +2309,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 	}
 	index->nulls_not_distinct = constraint->nulls_not_distinct;
 	index->isconstraint = true;
-	index->istemporal = constraint->without_overlaps != NULL;
+	index->iswithoutoverlaps = constraint->without_overlaps;
 	index->deferrable = constraint->deferrable;
 	index->initdeferred = constraint->initdeferred;
 
@@ -2555,6 +2555,10 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 			ListCell   *columns;
 			IndexElem  *iparam;
 
+			/* handled below */
+			if (constraint->without_overlaps && lc == list_last_cell(constraint->keys))
+				break;
+
 			/* Make sure referenced column exists. */
 			foreach(columns, cxt->columns)
 			{
@@ -2688,11 +2692,21 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		 * Anything in without_overlaps should be included,
 		 * but with the overlaps operator (&&) instead of equality.
 		 */
-		if (constraint->without_overlaps != NULL)
+		if (constraint->without_overlaps)
 		{
-			char *without_overlaps_str = strVal(constraint->without_overlaps);
+			char *without_overlaps_str = strVal(llast(constraint->keys));
 			IndexElem *iparam = makeNode(IndexElem);
 
+			/*
+			 * This enforces that there is at last one equality column besides
+			 * the WITHOUT OVERLAPS columns.  This is per SQL standard.  XXX
+			 * Do we need this?
+			 */
+			if (list_length(constraint->keys) < 2)
+				ereport(ERROR,
+						errcode(ERRCODE_SYNTAX_ERROR),
+						errmsg("constraint using WITHOUT OVERLAPS needs at least two columns"));
+
 			/*
 			 * Iterate through the table's columns
 			 * (like just a little bit above).
@@ -2855,7 +2869,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
  * and the new ones. Raises an error if we can't find one or it's not a range type.
  */
 static void
-validateWithoutOverlaps(CreateStmtContext *cxt, char *colname, int location)
+validateWithoutOverlaps(CreateStmtContext *cxt, const char *colname, int location)
 {
 	/* Check the new columns first in case their type is changing. */
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f2da27eb50..25ac0ad1bc 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -338,7 +338,7 @@ static char *deparse_expression_pretty(Node *expr, List *dpcontext,
 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, Oid indexId,
+static int	decompile_column_index_array(Datum column_index_array, Oid relId,
 										 bool withoutOverlaps, StringInfo buf);
 static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
 static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
@@ -2247,7 +2247,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 				val = SysCacheGetAttrNotNull(CONSTROID, tup,
 											 Anum_pg_constraint_conkey);
 
-				decompile_column_index_array(val, conForm->conrelid, conForm->conindid, false, &buf);
+				decompile_column_index_array(val, conForm->conrelid, false, &buf);
 
 				/* add foreign relation name */
 				appendStringInfo(&buf, ") REFERENCES %s(",
@@ -2258,7 +2258,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 				val = SysCacheGetAttrNotNull(CONSTROID, tup,
 											 Anum_pg_constraint_confkey);
 
-				decompile_column_index_array(val, conForm->confrelid, conForm->conindid, false, &buf);
+				decompile_column_index_array(val, conForm->confrelid, false, &buf);
 
 				appendStringInfoChar(&buf, ')');
 
@@ -2344,7 +2344,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 				if (!isnull)
 				{
 					appendStringInfoString(&buf, " (");
-					decompile_column_index_array(val, conForm->conrelid, InvalidOid, false, &buf);
+					decompile_column_index_array(val, conForm->conrelid, false, &buf);
 					appendStringInfoChar(&buf, ')');
 				}
 
@@ -2357,7 +2357,6 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 				Oid			indexId;
 				int			keyatts;
 				HeapTuple	indtup;
-				bool		isnull;
 
 				/* Start off the constraint definition */
 				if (conForm->contype == CONSTRAINT_PRIMARY)
@@ -2380,14 +2379,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 				val = SysCacheGetAttrNotNull(CONSTROID, tup,
 											 Anum_pg_constraint_conkey);
 
-				/*
-				 * If it has exclusion-style operator OIDs
-				 * then it uses WITHOUT OVERLAPS.
-				 */
-				indexId = conForm->conindid;
-				SysCacheGetAttr(CONSTROID, tup,
-						  Anum_pg_constraint_conexclop, &isnull);
-				keyatts = decompile_column_index_array(val, conForm->conrelid, indexId, !isnull, &buf);
+				keyatts = decompile_column_index_array(val, conForm->conrelid, conForm->conwithoutoverlaps, &buf);
 
 				appendStringInfoChar(&buf, ')');
 
@@ -2582,7 +2574,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
  * of keys.
  */
 static int
-decompile_column_index_array(Datum column_index_array, Oid relId, Oid indexId,
+decompile_column_index_array(Datum column_index_array, Oid relId,
 							 bool withoutOverlaps, StringInfo buf)
 {
 	Datum	   *keys;
@@ -2596,17 +2588,16 @@ decompile_column_index_array(Datum column_index_array, Oid relId, Oid indexId,
 	for (j = 0; j < nKeys; j++)
 	{
 		char	   *colName;
-		int			colid = DatumGetInt16(keys[j]);
 
-		colName = get_attname(relId, colid, false);
+		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
 			appendStringInfo(buf, ", %s", quote_identifier(colName));
 	}
+	if (withoutOverlaps)
+		appendStringInfoString(buf, " WITHOUT OVERLAPS");
 
 	return nKeys;
 }
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 45810ac35f..f341c5c370 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5568,12 +5568,14 @@ RelationGetIdentityKeyBitmap(Relation relation)
 /*
  * 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 or temporal primary key/unique
- * constraint. 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.
+ * This should be called only for an index that is known to have an associated
+ * exclusion constraint or primary key/unique constraint using WITHOUT
+ * OVERLAPS.
+
+ * 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.
  */
 void
 RelationGetExclusionInfo(Relation indexRelation,
@@ -5638,7 +5640,7 @@ RelationGetExclusionInfo(Relation indexRelation,
 
 		/* We want the exclusion constraint owning the index */
 		if ((conform->contype != CONSTRAINT_EXCLUSION &&
-					!(conform->contemporal && (
+					!(conform->conwithoutoverlaps && (
 							conform->contype == CONSTRAINT_PRIMARY
 							|| conform->contype == CONSTRAINT_UNIQUE))) ||
 			conform->conindid != RelationGetRelid(indexRelation))
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 54a15cf64a..c89b6da8b2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6983,14 +6983,14 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_conname,
 				i_condeferrable,
 				i_condeferred,
+				i_conwithoutoverlaps,
 				i_contableoid,
 				i_conoid,
 				i_condef,
 				i_tablespace,
 				i_indreloptions,
 				i_indstatcols,
-				i_indstatvals,
-				i_withoutoverlaps;
+				i_indstatvals;
 
 	/*
 	 * We want to perform just one query against pg_index.  However, we
@@ -7072,10 +7072,10 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 
 	if (fout->remoteVersion >= 170000)
 		appendPQExpBufferStr(query,
-							 "c.conexclop IS NOT NULL AS withoutoverlaps ");
+							 "c.conwithoutoverlaps ");
 	else
 		appendPQExpBufferStr(query,
-							 "null AS withoutoverlaps ");
+							 "NULL AS conwithoutoverlaps ");
 
 	/*
 	 * The point of the messy-looking outer join is to find a constraint that
@@ -7143,6 +7143,7 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	i_conname = PQfnumber(res, "conname");
 	i_condeferrable = PQfnumber(res, "condeferrable");
 	i_condeferred = PQfnumber(res, "condeferred");
+	i_conwithoutoverlaps = PQfnumber(res, "conwithoutoverlaps");
 	i_contableoid = PQfnumber(res, "contableoid");
 	i_conoid = PQfnumber(res, "conoid");
 	i_condef = PQfnumber(res, "condef");
@@ -7150,7 +7151,6 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 	i_indreloptions = PQfnumber(res, "indreloptions");
 	i_indstatcols = PQfnumber(res, "indstatcols");
 	i_indstatvals = PQfnumber(res, "indstatvals");
-	i_withoutoverlaps = PQfnumber(res, "withoutoverlaps");
 
 	indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
 
@@ -7251,9 +7251,9 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				constrinfo->conindex = indxinfo[j].dobj.dumpId;
 				constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
 				constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
+				constrinfo->conwithoutoverlaps = *(PQgetvalue(res, j, i_conwithoutoverlaps)) == 't';
 				constrinfo->conislocal = true;
 				constrinfo->separate = true;
-				constrinfo->withoutoverlaps = *(PQgetvalue(res, j, i_withoutoverlaps)) == 't';
 
 				indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
 			}
@@ -16949,23 +16949,12 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
 					break;
 				attname = getAttrName(indkey, tbinfo);
 
-				if (k == 0)
-				{
-					appendPQExpBuffer(q, "%s",
-										fmtId(attname));
-				}
-				else if (k == indxinfo->indnkeyattrs - 1 &&
-						coninfo->withoutoverlaps)
-				{
-					appendPQExpBuffer(q, ", %s WITHOUT OVERLAPS",
-									  fmtId(attname));
-				}
-				else
-				{
-					appendPQExpBuffer(q, ", %s",
-										fmtId(attname));
-				}
+				appendPQExpBuffer(q, "%s%s",
+								  (k == 0) ? "" : ", ",
+								  fmtId(attname));
 			}
+			if (coninfo->conwithoutoverlaps)
+				appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
 
 			if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
 				appendPQExpBufferStr(q, ") INCLUDE (");
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index ad7cc85b8c..cbf4b5e179 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -487,9 +487,9 @@ typedef struct _constraintInfo
 	DumpId		conindex;		/* identifies associated index if any */
 	bool		condeferrable;	/* true if constraint is DEFERRABLE */
 	bool		condeferred;	/* true if constraint is INITIALLY DEFERRED */
+	bool		conwithoutoverlaps;	/* true if the constraint is WITHOUT OVERLAPS */
 	bool		conislocal;		/* true if constraint has local definition */
 	bool		separate;		/* true if must dump as separate item */
-	bool		withoutoverlaps;	/* true if the last elem is WITHOUT OVERLAPS */
 } ConstraintInfo;
 
 typedef struct _procLangInfo
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index eb35d5ef60..8584a4b406 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -1015,12 +1015,11 @@
 		},
 	},
 
-	'ALTER TABLE ONLY test_table_tpk ADD CONSTRAINT ... PRIMARY KEY (..., ... WITHOUT OVERLAPS)' => {
+	'CONSTRAINT PRIMARY KEY / WITHOUT OVERLAPS' => {
 		create_sql  => 'CREATE TABLE dump_test.test_table_tpk (
 							col1 int4range,
 							col2 tstzrange,
-							CONSTRAINT test_table_tpk_pkey PRIMARY KEY
-								(col1, col2 WITHOUT OVERLAPS));',
+							CONSTRAINT test_table_tpk_pkey PRIMARY KEY (col1, col2 WITHOUT OVERLAPS));',
 		regexp => qr/^
 			\QALTER TABLE ONLY dump_test.test_table_tpk\E \n^\s+
 			\QADD CONSTRAINT test_table_tpk_pkey PRIMARY KEY (col1, col2 WITHOUT OVERLAPS);\E
@@ -1029,21 +1028,18 @@
 			%full_runs,
 			%dump_test_schema_runs,
 			section_post_data  => 1,
-			exclude_test_table => 1,
 		},
 		unlike => {
-			only_dump_test_table     => 1,
 			exclude_dump_test_schema => 1,
 			only_dump_measurement    => 1,
 		},
 	},
 
-	'ALTER TABLE ONLY test_table_tuq ADD CONSTRAINT ... UNIQUE (..., ... WITHOUT OVERLAPS)' => {
+	'CONSTRAINT UNIQUE / WITHOUT OVERLAPS' => {
 		create_sql  => 'CREATE TABLE dump_test.test_table_tuq (
 							col1 int4range,
 							col2 tstzrange,
-							CONSTRAINT test_table_tuq_uq UNIQUE
-								(col1, col2 WITHOUT OVERLAPS));',
+							CONSTRAINT test_table_tuq_uq UNIQUE (col1, col2 WITHOUT OVERLAPS));',
 		regexp => qr/^
 			\QALTER TABLE ONLY dump_test.test_table_tuq\E \n^\s+
 			\QADD CONSTRAINT test_table_tuq_uq UNIQUE (col1, col2 WITHOUT OVERLAPS);\E
@@ -1052,10 +1048,8 @@
 			%full_runs,
 			%dump_test_schema_runs,
 			section_post_data  => 1,
-			exclude_test_table => 1,
 		},
 		unlike => {
-			only_dump_test_table     => 1,
 			exclude_dump_test_schema => 1,
 			only_dump_measurement    => 1,
 		},
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4fbfc07460..135585411a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2384,10 +2384,10 @@ describeOneTableDetails(const char *schemaname,
 			else
 				appendPQExpBufferStr(&buf, ", false AS indisreplident");
 			appendPQExpBufferStr(&buf, ", c2.reltablespace");
-			if (pset.sversion >= 160000)
-				appendPQExpBufferStr(&buf, ", con.contemporal");
+			if (pset.sversion >= 170000)
+				appendPQExpBufferStr(&buf, ", con.conwithoutoverlaps");
 			else
-				appendPQExpBufferStr(&buf, ", false AS contemporal");
+				appendPQExpBufferStr(&buf, ", false AS conwithoutoverlaps");
 			appendPQExpBuffer(&buf,
 							  "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
 							  "  LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"
@@ -2410,8 +2410,8 @@ describeOneTableDetails(const char *schemaname,
 									  PQgetvalue(result, i, 0));
 
 					/*
-					 * If exclusion constraint or temporal PK/UNIQUE constraint,
-					 * print the constraintdef
+					 * If exclusion constraint or PK/UNIQUE constraint WITHOUT
+					 * OVERLAPS, print the constraintdef
 					 */
 					if (strcmp(PQgetvalue(result, i, 7), "x") == 0 ||
 							strcmp(PQgetvalue(result, i, 12), "t") == 0)
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 1a858c3897..0fccff9c05 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -92,7 +92,7 @@ extern Oid	index_create(Relation heapRelation,
 #define	INDEX_CONSTR_CREATE_INIT_DEFERRED	(1 << 2)
 #define	INDEX_CONSTR_CREATE_UPDATE_INDEX	(1 << 3)
 #define	INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS	(1 << 4)
-#define	INDEX_CONSTR_CREATE_TEMPORAL		(1 << 5)
+#define	INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS (1 << 5)
 
 extern Oid	index_concurrently_create_copy(Relation heapRelation,
 										   Oid oldIndexId,
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 98a560741d..c17b1e9d31 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -108,10 +108,10 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
 	bool		connoinherit;
 
 	/*
-	 * For primary and foreign keys, signifies the last column is a range
-	 * and should use overlaps instead of equals.
+	 * For primary keys and unique constraints, signifies the last column uses
+	 * overlaps instead of equals.
 	 */
-	bool		contemporal;
+	bool		conwithoutoverlaps;
 
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 
@@ -152,7 +152,8 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
 
 	/*
 	 * If an exclusion constraint, the OIDs of the exclusion operators for
-	 * each column of the constraint. Also set for temporal primary keys.
+	 * each column of the constraint.  Also set for unique constraints/primary
+	 * keys using WITHOUT OVERLAPS.
 	 */
 	Oid			conexclop[1] BKI_LOOKUP(pg_operator);
 
@@ -242,7 +243,7 @@ extern Oid	CreateConstraintEntry(const char *constraintName,
 								  bool conIsLocal,
 								  int conInhCount,
 								  bool conNoInherit,
-								  bool conTemporal,
+								  bool conWithoutOverlaps,
 								  bool is_internal);
 
 extern bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index f10886f6ca..25f55c181b 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -45,7 +45,7 @@ extern bool CheckIndexCompatible(Oid oldId,
 								 const char *accessMethodName,
 								 const List *attributeList,
 								 const List *exclusionOpNames,
-								 bool istemporal);
+								 bool isWithoutOverlaps);
 extern Oid	GetDefaultOpClass(Oid type_id, Oid am_id);
 extern Oid	ResolveOpClass(const List *opclass, Oid attrType,
 						   const char *accessMethodName, Oid accessMethodId);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ff65db2db1..098990dd16 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2589,6 +2589,7 @@ typedef struct Constraint
 	bool		nulls_not_distinct; /* null treatment for UNIQUE constraints */
 	List	   *keys;			/* String nodes naming referenced key
 								 * column(s); also used for NOT NULL */
+	bool		without_overlaps;	/* WITHOUT OVERLAPS specified */
 	List	   *including;		/* String nodes naming referenced nonkey
 								 * column(s) */
 
@@ -2617,9 +2618,6 @@ typedef struct Constraint
 	Oid			old_pktable_oid;	/* pg_constraint.confrelid of my former
 									 * self */
 
-	/* Fields used for temporal PRIMARY KEY and FOREIGN KEY constraints: */
-	Node	   *without_overlaps; /* String node naming range column */
-
 	/* Fields used for constraints that allow a NOT VALID specification */
 	bool		skip_validation;	/* skip validation of existing rows? */
 	bool		initially_valid;	/* mark the new constraint as valid? */
@@ -3221,7 +3219,7 @@ typedef struct IndexStmt
 	bool		nulls_not_distinct; /* null treatment for UNIQUE constraints */
 	bool		primary;		/* is index a primary key? */
 	bool		isconstraint;	/* is it for a pkey/unique constraint? */
-	bool		istemporal;		/* is it for a temporal pkey? */
+	bool		iswithoutoverlaps;	/* is the constraint WITHOUT OVERLAPS? */
 	bool		deferrable;		/* is the constraint DEFERRABLE? */
 	bool		initdeferred;	/* is the constraint INITIALLY DEFERRED? */
 	bool		transformed;	/* true when transformIndexStmt is finished */
diff --git a/src/test/regress/expected/without_overlaps.out b/src/test/regress/expected/without_overlaps.out
index 36aebc452f..e565490a38 100644
--- a/src/test/regress/expected/without_overlaps.out
+++ b/src/test/regress/expected/without_overlaps.out
@@ -11,9 +11,7 @@ CREATE TABLE temporal_rng (
 	valid_at tsrange,
 	CONSTRAINT temporal_rng_pk PRIMARY KEY (valid_at WITHOUT OVERLAPS)
 );
-ERROR:  syntax error at or near "WITHOUT"
-LINE 3:  CONSTRAINT temporal_rng_pk PRIMARY KEY (valid_at WITHOUT OV...
-                                                          ^
+ERROR:  constraint using WITHOUT OVERLAPS needs at least two columns
 -- PK with a range column/PERIOD that isn't there:
 CREATE TABLE temporal_rng (
 	id INTEGER,
@@ -106,9 +104,7 @@ CREATE TABLE temporal_rng3 (
 	valid_at tsrange,
 	CONSTRAINT temporal_rng3_uq UNIQUE (valid_at WITHOUT OVERLAPS)
 );
-ERROR:  syntax error at or near "WITHOUT"
-LINE 3:  CONSTRAINT temporal_rng3_uq UNIQUE (valid_at WITHOUT OVERLA...
-                                                      ^
+ERROR:  constraint using WITHOUT OVERLAPS needs at least two columns
 -- UNIQUE with a range column/PERIOD that isn't there:
 CREATE TABLE temporal_rng3 (
 	id INTEGER,
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 16683148d2..b08aaae44b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -28,7 +28,7 @@ test: strings md5 numerology point lseg line box path polygon circle date time t
 # geometry depends on point, lseg, line, box, path, polygon, circle
 # horology depends on date, time, timetz, timestamp, timestamptz, interval
 # ----------
-test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc without_overlaps
+test: geometry horology tstypes regex type_sanity opr_sanity misc_sanity comments expressions unicode xid mvcc
 
 # ----------
 # Load huge amounts of data
@@ -78,7 +78,7 @@ test: brin_bloom brin_multi
 # psql depends on create_am
 # amutils depends on geometry, create_index_spgist, hash_index, brin
 # ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps
 
 # collate.*.utf8 tests cannot be run in parallel with each other
 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252
-- 
2.42.0