> What this patch does is to add two new clauses for FROM-list items,
> NORMALIZE and ALIGN, which reshuffle a set of ranges into a new list
> that can then be aggregated more easily.  From the original message:
>
> > For NORMALIZE the tuples' ranges need to be split into all sub-ranges
> > according to all matching ranges of the second relation. For this we
> > create a subquery that first joins one relation with the range
> > boundaries of the other and then sorts the result. The executor
> > function splits the ranges in a sweep-line based manner.
> >
> > For ALIGN the tuples' ranges must be split into all intersections and
> > differences with the other relation according to the join condition.
> > For this we create a subquery that first joins the two relations and
> > then sorts the result. The executor function splits the ranges
> > accordingly in a sweep-line based manner.
>
> So there isn't really temporal query processing as such here, only some
> helpers that can make it easier.

The goal of temporal aligners and normalizers is to split ranges to allow a
reduction from temporal queries to their non-temporal counterparts.
Splitting
ranges is necessary for temporal query processing. Temporal aligners and
normalizer may then be used as building-blocks for any temporal query
construct.

> I can see how those operations can be useful, but it would help if there
> were a more formal definition to be able to check that further.

We have published two papers, that contain formal definitions and related
work
for the temporal aligner and normalizer. Please see [1] and [2].

> What I'm missing here is some references: existing implementations,
> standards, documentation, research papers, alternative ideas, rejected
> alternatives, etc.

A good overview of existing implementations in DBMSs, SQL standard, and
history
is given in [3].

> Also, the submission is missing documentation and test cases.  There are
> technical terms used in the code that I don't understand.

We added a second patch with test cases and expected results. We are now
writing the documentation in sgml-format.

> I think there are probably many interesting applications for normalizing
> or otherwise adjusting ranges.  I'd like to see an overview and
> consideration of other applications.

Please see the attached file adjustment.sql for some interesting
applications.

> Ideally, I'd like to see these things implemented as some kind of
> user-space construct, like an operator or function.  I think we'd need a
> clearer definition of what it is they do before we can evaluate that.

Can you please explain what you mean by "user-space construct" in this case.



Best regards,
Anton, Johann, Michael, Peter

----
[1] Anton Dignös, Michael H. Böhlen, Johann Gamper:
Temporal alignment. SIGMOD Conference 2012: 433-444
http://doi.acm.org/10.1145/2213836.2213886
[2] Anton Dignös, Michael H. Böhlen, Johann Gamper, Christian S. Jensen:
Extending the Kernel of a Relational DBMS with Comprehensive Support for
Sequenced Temporal Queries. ACM Trans. Database Syst. 41(4): 26:1-26:46
(2016)
http://doi.acm.org/10.1145/2967608
[3] https://www2.cs.arizona.edu/people/rts/sql3.html and
https://www2.cs.arizona.edu/people/rts/tsql2.html

Attachment: adjustment.sql
Description: application/sql

diff --git src/test/regress/expected/temporal_primitives.out src/test/regress/expected/temporal_primitives.out
new file mode 100644
index 0000000..6e4cc0d
--- /dev/null
+++ src/test/regress/expected/temporal_primitives.out
@@ -0,0 +1,841 @@
+--
+-- TEMPORAL PRIMITIVES
+--
+SET datestyle TO ymd;
+CREATE COLLATION "de_DE.utf8" (LC_COLLATE = "de_DE.utf8",
+                               LC_CTYPE = "de_DE.utf8" );
+CREATE TEMP TABLE tpg_table1 (a char, b char, ts int, te int);
+CREATE TEMP TABLE tpg_table2 (c int, d char, ts int, te int);
+INSERT INTO tpg_table1 VALUES
+('a','B',1,7),
+('b','B',3,9),
+('c','G',8,10);
+INSERT INTO tpg_table2 VALUES
+(1,'B',2,5),
+(2,'B',3,4),
+(3,'B',7,9);
+-- VALID TIME columns (i.e., ts and te) are no longer at the end of the
+-- targetlist.
+CREATE TEMP TABLE tpg_table3 AS
+	SELECT a, ts, te, b FROM tpg_table1;
+CREATE TEMP TABLE tpg_table4 AS
+	SELECT c, ts, d, te FROM tpg_table2;
+-- VALID TIME columns represented as range type
+CREATE TEMP TABLE tpg_table5 AS
+	SELECT int4range(ts, te) t, a, b FROM tpg_table1;
+CREATE TEMP TABLE tpg_table6 AS
+	SELECT int4range(ts, te) t, c a, d b FROM tpg_table2;
+-- VALID TIME columns as VARCHARs
+CREATE TEMP TABLE tpg_table7 (a int, ts varchar, te varchar);
+CREATE TEMP TABLE tpg_table8 (a int,
+							  ts varchar COLLATE "de_DE.utf8",
+							  te varchar COLLATE "POSIX");
+INSERT INTO tpg_table7 VALUES
+(0, 'A', 'D'),
+(1, 'C', 'X'),
+(0, 'ABC', 'BCD'),
+(0, 'xABC', 'xBCD'),
+(0, 'BAA', 'BBB');
+INSERT INTO tpg_table8 VALUES
+(0, 'A', 'D'),
+(1, 'C', 'X');
+-- Tables to check different data types, and corner cases
+CREATE TEMP TABLE tpg_table9 (a int, ts timestamp, te timestamp);
+CREATE TEMP TABLE tpg_table10 (a int, ts double precision, te double precision);
+CREATE TEMP TABLE tpg_table11 AS TABLE tpg_table10;
+INSERT INTO tpg_table9 VALUES
+(0, '2000-01-01', '2000-01-10'),
+(1, '2000-01-05', '2000-01-20');
+INSERT INTO tpg_table10 VALUES
+(0, 1.0, 1.1111),
+(1, 1.11109999, 2.0);
+INSERT INTO tpg_table11 VALUES
+(0, 1.0, 'Infinity'),
+(1, '-Infinity', 2.0);
+--
+-- TEMPORAL ALIGNER: BASICS
+--
+-- Equality qualifiers
+SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  5
+ a | B |  3 |  4
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  3 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(9 rows)
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON tpg_table1.b = tpg_table2.d
+		WITH (tpg_table1.ts, tpg_table1.te, tpg_table2.ts, tpg_table2.te)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  5
+ a | B |  3 |  4
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  3 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(9 rows)
+
+-- Alignment with aggregation
+-- NB: Targetlist of outer query is *not* A_STAR...
+SELECT a, COUNT(a) FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+ a | count 
+---+-------
+ a |     4
+ b |     4
+ c |     1
+(3 rows)
+
+-- Equality qualifiers
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 ALIGN tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+ a | ts | te | b 
+---+----+----+---
+ a |  1 |  2 | B
+ a |  2 |  5 | B
+ a |  3 |  4 | B
+ a |  5 |  7 | B
+ b |  3 |  4 | B
+ b |  3 |  5 | B
+ b |  5 |  7 | B
+ b |  7 |  9 | B
+ c |  8 | 10 | G
+(9 rows)
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 ALIGN tpg_table4
+		ON tpg_table3.b = tpg_table4.d
+		WITH (tpg_table3.ts, tpg_table3.te, tpg_table4.ts, tpg_table4.te)
+	) x;
+ a | ts | te | b 
+---+----+----+---
+ a |  1 |  2 | B
+ a |  2 |  5 | B
+ a |  3 |  4 | B
+ a |  5 |  7 | B
+ b |  3 |  4 | B
+ b |  3 |  5 | B
+ b |  5 |  7 | B
+ b |  7 |  9 | B
+ c |  8 | 10 | G
+(9 rows)
+
+-- Alignment with aggregation where targetlist of outer query is *not* A_STAR...
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT a, COUNT(a) FROM (
+	tpg_table3 ALIGN tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+ a | count 
+---+-------
+ a |     4
+ b |     4
+ c |     1
+(3 rows)
+
+-- Test relations with differently named temporal bound attributes and relation
+-- and column aliases.
+SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2 x(c,d,s,e)
+		ON b = d
+		WITH (ts, te, s, e)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  5
+ a | B |  3 |  4
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  3 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(9 rows)
+
+-- Range types for temporal boundaries, i.e., valid time columns
+SELECT * FROM (
+	tpg_table5 ALIGN tpg_table6
+		ON TRUE
+		WITH (t, t)
+	) x;
+   t    | a | b 
+--------+---+---
+ [1,2)  | a | B
+ [2,5)  | a | B
+ [3,4)  | a | B
+ [5,7)  | a | B
+ [3,4)  | b | B
+ [3,5)  | b | B
+ [5,7)  | b | B
+ [7,9)  | b | B
+ [8,9)  | c | G
+ [9,10) | c | G
+(10 rows)
+
+--
+-- TEMPORAL ALIGNER: TEMPORAL JOIN EXAMPLE
+--
+-- Full temporal join example with absorbing where clause, timestamp
+-- propagation (see CTEs targetlists with V and U) and range types
+WITH t1 AS (SELECT *, t u FROM tpg_table5),
+	 t2 AS (SELECT *, t v FROM tpg_table6)
+SELECT t, b, x.a, y.a FROM (
+	t1 ALIGN t2
+		ON t1.b = t2.b
+		WITH (t, t)
+	) x
+	LEFT OUTER JOIN (
+		SELECT * FROM (
+		t2 ALIGN t1
+			ON t1.b = t2.b
+			WITH (t, t)
+		) y
+	) y
+	USING (b, t)
+	WHERE (
+			(lower(t) = lower(u) OR lower(t) = lower(v))
+			AND
+			(upper(t) = upper(u) OR upper(t) = upper(v))
+		)
+		OR u IS NULL
+		OR v IS NULL
+	ORDER BY 1,2,3,4;
+   t    | b | a | a 
+--------+---+---+---
+ [1,2)  | B | a |  
+ [2,5)  | B | a | 1
+ [3,4)  | B | a | 2
+ [3,4)  | B | b | 2
+ [3,5)  | B | b | 1
+ [5,7)  | B | a |  
+ [5,7)  | B | b |  
+ [7,9)  | B | b | 3
+ [8,10) | G | c |  
+(9 rows)
+
+-- Full temporal join example with absorbing where clause, timestamp
+-- propagation (see CTEs targetlists with V and U) and scalar VALID TIME columns
+WITH t1 AS (SELECT *, ts us, te ue FROM tpg_table1),
+	 t2 AS (SELECT *, ts vs, te ve FROM tpg_table2)
+SELECT x.ts, x.te, b, a, c FROM (
+	t1 ALIGN t2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	LEFT OUTER JOIN (
+		SELECT * FROM (
+		t2 ALIGN t1
+			ON b = d
+			WITH (ts, te, ts, te)
+		) y
+	) y
+	ON b = d AND x.ts = y.ts AND x.te = y.te
+	WHERE (
+			(x.ts = us OR x.ts = vs)
+			AND
+			(x.te = ue OR x.te = ve)
+		)
+		OR us IS NULL
+		OR vs IS NULL
+	ORDER BY 1,2,3,4;
+ ts | te | b | a | c 
+----+----+---+---+---
+  1 |  2 | B | a |  
+  2 |  5 | B | a | 1
+  3 |  4 | B | a | 2
+  3 |  4 | B | b | 2
+  3 |  5 | B | b | 1
+  5 |  7 | B | a |  
+  5 |  7 | B | b |  
+  7 |  9 | B | b | 3
+  8 | 10 | G | c |  
+(9 rows)
+
+-- Collation and varchar boundaries
+SELECT * FROM (
+	tpg_table7 x ALIGN tpg_table7 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+ a |  ts  |  te  
+---+------+------
+ 0 | A    | D
+ 0 | ABC  | BCD
+ 0 | BAA  | BBB
+ 0 | C    | D
+ 1 | C    | D
+ 1 | C    | X
+ 0 | ABC  | BCD
+ 0 | BAA  | BBB
+ 0 | xABC | xBCD
+ 0 | BAA  | BBB
+(10 rows)
+
+-- Collation and varchar boundaries with incompatible collations (ERROR expected)
+SELECT * FROM (
+	tpg_table8 x ALIGN tpg_table8 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+ERROR:  could not determine which collation to use for string comparison
+HINT:  Use the COLLATE clause to set the collation explicitly.
+--
+-- TEMPORAL ALIGNER: SELECTION PUSH-DOWN
+--
+-- VALID TIME columns are not safe to be pushed down, for the rest everything
+-- should work as usual.
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 ALIGN tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3;
+                                                          QUERY PLAN                                                          
+------------------------------------------------------------------------------------------------------------------------------
+ Subquery Scan on x
+   ->  Adjustment(for ALIGN)
+         ->  Sort
+               Sort Key: (row_id() OVER (?)), (GREATEST(tpg_table2.ts, tpg_table1.ts)), (LEAST(tpg_table2.te, tpg_table1.te))
+               ->  Nested Loop Left Join
+                     Join Filter: ((tpg_table2.ts < tpg_table1.te) AND (tpg_table2.te > tpg_table1.ts))
+                     ->  WindowAgg
+                           ->  Seq Scan on tpg_table2
+                                 Filter: (c < 3)
+                     ->  Materialize
+                           ->  Seq Scan on tpg_table1
+(11 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 ALIGN tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 AND ts > 3;
+                                                          QUERY PLAN                                                          
+------------------------------------------------------------------------------------------------------------------------------
+ Subquery Scan on x
+   Filter: (x.ts > 3)
+   ->  Adjustment(for ALIGN)
+         ->  Sort
+               Sort Key: (row_id() OVER (?)), (GREATEST(tpg_table2.ts, tpg_table1.ts)), (LEAST(tpg_table2.te, tpg_table1.te))
+               ->  Nested Loop Left Join
+                     Join Filter: ((tpg_table2.ts < tpg_table1.te) AND (tpg_table2.te > tpg_table1.ts))
+                     ->  WindowAgg
+                           ->  Seq Scan on tpg_table2
+                                 Filter: (c < 3)
+                     ->  Materialize
+                           ->  Seq Scan on tpg_table1
+(12 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 ALIGN tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 OR ts > 3;
+                                                          QUERY PLAN                                                          
+------------------------------------------------------------------------------------------------------------------------------
+ Subquery Scan on x
+   Filter: ((x.c < 3) OR (x.ts > 3))
+   ->  Adjustment(for ALIGN)
+         ->  Sort
+               Sort Key: (row_id() OVER (?)), (GREATEST(tpg_table2.ts, tpg_table1.ts)), (LEAST(tpg_table2.te, tpg_table1.te))
+               ->  Nested Loop Left Join
+                     Join Filter: ((tpg_table2.ts < tpg_table1.te) AND (tpg_table2.te > tpg_table1.ts))
+                     ->  WindowAgg
+                           ->  Seq Scan on tpg_table2
+                     ->  Materialize
+                           ->  Seq Scan on tpg_table1
+(11 rows)
+
+--
+-- TEMPORAL ALIGNER: DATA TYPES
+--
+-- Data types: Timestamps
+-- We use to_char here to be sure that we have the same output format on all
+-- platforms and locale configuration
+SELECT a, to_char(ts, 'YYYY-MM-DD') ts, to_char(te, 'YYYY-MM-DD') te FROM (
+	tpg_table9 t1 ALIGN tpg_table9 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+ a |     ts     |     te     
+---+------------+------------
+ 0 | 2000-01-01 | 2000-01-10
+ 0 | 2000-01-05 | 2000-01-10
+ 1 | 2000-01-05 | 2000-01-20
+(3 rows)
+
+-- Data types: Double precision
+SELECT a, ts, te FROM (
+	tpg_table10 t1 ALIGN tpg_table10 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+ a |     ts     |   te   
+---+------------+--------
+ 0 |          1 | 1.1111
+ 0 | 1.11109999 | 1.1111
+ 1 | 1.11109999 |      2
+(3 rows)
+
+-- Data types: Double precision with +/- infinity
+SELECT a, ts, te FROM (
+	tpg_table11 t1 ALIGN tpg_table11 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+ a |    ts     |    te    
+---+-----------+----------
+ 0 |         1 |        2
+ 0 |         1 | Infinity
+ 1 | -Infinity |        2
+(3 rows)
+
+--
+-- TEMPORAL NORMALIZER: BASICS
+--
+-- Equality qualifiers
+SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  3
+ a | B |  3 |  4
+ a | B |  4 |  5
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  4 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(10 rows)
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON tpg_table1.b = tpg_table2.d
+		WITH (tpg_table1.ts, tpg_table1.te, tpg_table2.ts, tpg_table2.te)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  3
+ a | B |  3 |  4
+ a | B |  4 |  5
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  4 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(10 rows)
+
+-- Alignment with aggregation
+-- NB: Targetlist of outer query is *not* A_STAR...
+SELECT a, COUNT(a) FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+ a | count 
+---+-------
+ a |     5
+ b |     4
+ c |     1
+(3 rows)
+
+-- Equality qualifiers
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 NORMALIZE tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+ a | ts | te | b 
+---+----+----+---
+ a |  1 |  2 | B
+ a |  2 |  3 | B
+ a |  3 |  4 | B
+ a |  4 |  5 | B
+ a |  5 |  7 | B
+ b |  3 |  4 | B
+ b |  4 |  5 | B
+ b |  5 |  7 | B
+ b |  7 |  9 | B
+ c |  8 | 10 | G
+(10 rows)
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 NORMALIZE tpg_table4
+		ON tpg_table3.b = tpg_table4.d
+		WITH (tpg_table3.ts, tpg_table3.te, tpg_table4.ts, tpg_table4.te)
+	) x;
+ a | ts | te | b 
+---+----+----+---
+ a |  1 |  2 | B
+ a |  2 |  3 | B
+ a |  3 |  4 | B
+ a |  4 |  5 | B
+ a |  5 |  7 | B
+ b |  3 |  4 | B
+ b |  4 |  5 | B
+ b |  5 |  7 | B
+ b |  7 |  9 | B
+ c |  8 | 10 | G
+(10 rows)
+
+-- Alignment with aggregation where targetlist of outer query is *not* A_STAR...
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT a, COUNT(a) FROM (
+	tpg_table3 NORMALIZE tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+ a | count 
+---+-------
+ a |     5
+ b |     4
+ c |     1
+(3 rows)
+
+-- Test relations with differently named temporal bound attributes and relation
+-- and column aliases.
+SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2 x(c,d,s,e)
+		ON b = d
+		WITH (ts, te, s, e)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  3
+ a | B |  3 |  4
+ a | B |  4 |  5
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  4 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(10 rows)
+
+-- Normalizer's USING clause (self-normalization)
+SELECT * FROM (
+	tpg_table1 t1 NORMALIZE tpg_table1 t2
+		USING (a)
+		WITH (ts, te, ts, te)
+	) x;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  7
+ b | B |  3 |  9
+ c | G |  8 | 10
+(3 rows)
+
+-- Range types for temporal boundaries, i.e., valid time columns
+SELECT * FROM (
+	tpg_table5 NORMALIZE tpg_table6
+		USING (b)
+		WITH (t, t)
+	) x;
+   t    | a | b 
+--------+---+---
+ [1,2)  | a | B
+ [2,3)  | a | B
+ [3,4)  | a | B
+ [4,5)  | a | B
+ [5,7)  | a | B
+ [3,4)  | b | B
+ [4,5)  | b | B
+ [5,7)  | b | B
+ [7,9)  | b | B
+ [8,10) | c | G
+(10 rows)
+
+-- Collation and varchar boundaries
+SELECT * FROM (
+	tpg_table7 x NORMALIZE tpg_table7 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+ a |  ts  |  te  
+---+------+------
+ 0 | A    | ABC
+ 0 | ABC  | BAA
+ 0 | BAA  | BBB
+ 0 | BBB  | BCD
+ 0 | BCD  | C
+ 0 | C    | D
+ 1 | C    | D
+ 1 | D    | X
+ 0 | ABC  | BAA
+ 0 | BAA  | BBB
+ 0 | BBB  | BCD
+ 0 | xABC | xBCD
+ 0 | BAA  | BBB
+(13 rows)
+
+-- Collation and varchar boundaries with incompatible collations (ERROR expected)
+SELECT * FROM (
+	tpg_table8 x NORMALIZE tpg_table8 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+ERROR:  could not determine which collation to use for string comparison
+HINT:  Use the COLLATE clause to set the collation explicitly.
+--
+-- TEMPORAL NORMALIZER: SELECTION PUSH-DOWN
+--
+-- VALID TIME columns are not safe to be pushed down, for the rest everything
+-- should work as usual.
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 NORMALIZE tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3;
+                                               QUERY PLAN                                                
+---------------------------------------------------------------------------------------------------------
+ Subquery Scan on x
+   ->  Adjustment(for NORMALIZE)
+         ->  Sort
+               Sort Key: (row_id() OVER (?)), tpg_table1.ts
+               ->  Nested Loop Left Join
+                     Join Filter: ((tpg_table1.ts >= tpg_table2.ts) AND (tpg_table1.ts < tpg_table2.te))
+                     ->  WindowAgg
+                           ->  Seq Scan on tpg_table2
+                                 Filter: (c < 3)
+                     ->  Materialize
+                           ->  Append
+                                 ->  Seq Scan on tpg_table1
+                                 ->  Seq Scan on tpg_table1 tpg_table1_1
+(13 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 NORMALIZE tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 AND ts > 3;
+                                               QUERY PLAN                                                
+---------------------------------------------------------------------------------------------------------
+ Subquery Scan on x
+   Filter: (x.ts > 3)
+   ->  Adjustment(for NORMALIZE)
+         ->  Sort
+               Sort Key: (row_id() OVER (?)), tpg_table1.ts
+               ->  Nested Loop Left Join
+                     Join Filter: ((tpg_table1.ts >= tpg_table2.ts) AND (tpg_table1.ts < tpg_table2.te))
+                     ->  WindowAgg
+                           ->  Seq Scan on tpg_table2
+                                 Filter: (c < 3)
+                     ->  Materialize
+                           ->  Append
+                                 ->  Seq Scan on tpg_table1
+                                 ->  Seq Scan on tpg_table1 tpg_table1_1
+(14 rows)
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 NORMALIZE tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 OR ts > 3;
+                                               QUERY PLAN                                                
+---------------------------------------------------------------------------------------------------------
+ Subquery Scan on x
+   Filter: ((x.c < 3) OR (x.ts > 3))
+   ->  Adjustment(for NORMALIZE)
+         ->  Sort
+               Sort Key: (row_id() OVER (?)), tpg_table1.ts
+               ->  Nested Loop Left Join
+                     Join Filter: ((tpg_table1.ts >= tpg_table2.ts) AND (tpg_table1.ts < tpg_table2.te))
+                     ->  WindowAgg
+                           ->  Seq Scan on tpg_table2
+                     ->  Materialize
+                           ->  Append
+                                 ->  Seq Scan on tpg_table1
+                                 ->  Seq Scan on tpg_table1 tpg_table1_1
+(13 rows)
+
+--
+-- TEMPORAL NORMALIZER: DATA TYPES
+--
+-- Data types: Timestamps
+-- We use to_char here to be sure that we have the same output format on all
+-- platforms and locale configuration
+SELECT a, to_char(ts, 'YYYY-MM-DD') ts, to_char(te, 'YYYY-MM-DD') te FROM (
+	tpg_table9 t1 NORMALIZE tpg_table9 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+ a |     ts     |     te     
+---+------------+------------
+ 0 | 2000-01-01 | 2000-01-05
+ 0 | 2000-01-05 | 2000-01-10
+ 1 | 2000-01-05 | 2000-01-20
+(3 rows)
+
+-- Data types: Double precision
+SELECT a, ts, te FROM (
+	tpg_table10 t1 NORMALIZE tpg_table10 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+ a |     ts     |     te     
+---+------------+------------
+ 0 |          1 | 1.11109999
+ 0 | 1.11109999 |     1.1111
+ 1 | 1.11109999 |          2
+(3 rows)
+
+-- Data types: Double precision with +/- infinity
+SELECT a, ts, te FROM (
+	tpg_table11 t1 NORMALIZE tpg_table11 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+ a |    ts     |    te    
+---+-----------+----------
+ 0 |         1 |        2
+ 0 |         2 | Infinity
+ 1 | -Infinity |        2
+(3 rows)
+
+--
+-- TEMPORAL ALIGNER AND NORMALIZER: VIEWS
+--
+-- Views with temporal normalization
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+TABLE v;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  3
+ a | B |  3 |  4
+ a | B |  4 |  5
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  4 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(10 rows)
+
+DROP VIEW v;
+-- Views with temporal alignment
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+TABLE v;
+ a | b | ts | te 
+---+---+----+----
+ a | B |  1 |  2
+ a | B |  2 |  5
+ a | B |  3 |  4
+ a | B |  5 |  7
+ b | B |  3 |  4
+ b | B |  3 |  5
+ b | B |  5 |  7
+ b | B |  7 |  9
+ c | G |  8 | 10
+(9 rows)
+
+DROP VIEW v;
+-- Testing temporal normalization with ambiguous columns, i.e. columns that
+-- are used internally...
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 AS r(p1, p1_0, "p1_-1", p1_1) NORMALIZE tpg_table2 s
+		ON r.p1_0 = s.d
+		WITH ("p1_-1", p1_1, ts, te)
+	) x;
+TABLE v;
+ p1 | p1_0 | p1_-1 | p1_1 
+----+------+-------+------
+ a  | B    |     1 |    2
+ a  | B    |     2 |    3
+ a  | B    |     3 |    4
+ a  | B    |     4 |    5
+ a  | B    |     5 |    7
+ b  | B    |     3 |    4
+ b  | B    |     4 |    5
+ b  | B    |     5 |    7
+ b  | B    |     7 |    9
+ c  | G    |     8 |   10
+(10 rows)
+
+DROP VIEW v;
+-- Testing temporal alignment with ambiguous columns, i.e. columns that
+-- are used internally...
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 AS r(p1, p1_0, "p1_-1", p1_1) ALIGN tpg_table2 s
+		ON r.p1_0 = s.d
+		WITH ("p1_-1",p1_1,ts,te)
+	) x;
+TABLE v;
+ p1 | p1_0 | p1_-1 | p1_1 
+----+------+-------+------
+ a  | B    |     1 |    2
+ a  | B    |     2 |    5
+ a  | B    |     3 |    4
+ a  | B    |     5 |    7
+ b  | B    |     3 |    4
+ b  | B    |     3 |    5
+ b  | B    |     5 |    7
+ b  | B    |     7 |    9
+ c  | G    |     8 |   10
+(9 rows)
+
+DROP VIEW v;
diff --git src/test/regress/parallel_schedule src/test/regress/parallel_schedule
index 8641769..61813ef 100644
--- src/test/regress/parallel_schedule
+++ src/test/regress/parallel_schedule
@@ -89,7 +89,7 @@ test: brin gin gist spgist privileges init_privs security_label collate matview
 # ----------
 # Another group of parallel tests
 # ----------
-test: alter_generic alter_operator misc psql async dbsize misc_functions tsrf
+test: alter_generic alter_operator misc psql async dbsize misc_functions tsrf temporal_primitives
 
 # rules cannot run concurrently with any test that creates a view
 test: rules psql_crosstab amutils
diff --git src/test/regress/sql/temporal_primitives.sql src/test/regress/sql/temporal_primitives.sql
new file mode 100644
index 0000000..9b475f7
--- /dev/null
+++ src/test/regress/sql/temporal_primitives.sql
@@ -0,0 +1,456 @@
+--
+-- TEMPORAL PRIMITIVES
+--
+SET datestyle TO ymd;
+
+CREATE COLLATION "de_DE.utf8" (LC_COLLATE = "de_DE.utf8",
+                               LC_CTYPE = "de_DE.utf8" );
+
+CREATE TEMP TABLE tpg_table1 (a char, b char, ts int, te int);
+CREATE TEMP TABLE tpg_table2 (c int, d char, ts int, te int);
+
+INSERT INTO tpg_table1 VALUES
+('a','B',1,7),
+('b','B',3,9),
+('c','G',8,10);
+INSERT INTO tpg_table2 VALUES
+(1,'B',2,5),
+(2,'B',3,4),
+(3,'B',7,9);
+
+-- VALID TIME columns (i.e., ts and te) are no longer at the end of the
+-- targetlist.
+CREATE TEMP TABLE tpg_table3 AS
+	SELECT a, ts, te, b FROM tpg_table1;
+CREATE TEMP TABLE tpg_table4 AS
+	SELECT c, ts, d, te FROM tpg_table2;
+
+-- VALID TIME columns represented as range type
+CREATE TEMP TABLE tpg_table5 AS
+	SELECT int4range(ts, te) t, a, b FROM tpg_table1;
+CREATE TEMP TABLE tpg_table6 AS
+	SELECT int4range(ts, te) t, c a, d b FROM tpg_table2;
+
+-- VALID TIME columns as VARCHARs
+CREATE TEMP TABLE tpg_table7 (a int, ts varchar, te varchar);
+CREATE TEMP TABLE tpg_table8 (a int,
+							  ts varchar COLLATE "de_DE.utf8",
+							  te varchar COLLATE "POSIX");
+
+INSERT INTO tpg_table7 VALUES
+(0, 'A', 'D'),
+(1, 'C', 'X'),
+(0, 'ABC', 'BCD'),
+(0, 'xABC', 'xBCD'),
+(0, 'BAA', 'BBB');
+
+INSERT INTO tpg_table8 VALUES
+(0, 'A', 'D'),
+(1, 'C', 'X');
+
+-- Tables to check different data types, and corner cases
+CREATE TEMP TABLE tpg_table9 (a int, ts timestamp, te timestamp);
+CREATE TEMP TABLE tpg_table10 (a int, ts double precision, te double precision);
+CREATE TEMP TABLE tpg_table11 AS TABLE tpg_table10;
+
+INSERT INTO tpg_table9 VALUES
+(0, '2000-01-01', '2000-01-10'),
+(1, '2000-01-05', '2000-01-20');
+
+INSERT INTO tpg_table10 VALUES
+(0, 1.0, 1.1111),
+(1, 1.11109999, 2.0);
+
+INSERT INTO tpg_table11 VALUES
+(0, 1.0, 'Infinity'),
+(1, '-Infinity', 2.0);
+
+
+--
+-- TEMPORAL ALIGNER: BASICS
+--
+
+-- Equality qualifiers
+SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON tpg_table1.b = tpg_table2.d
+		WITH (tpg_table1.ts, tpg_table1.te, tpg_table2.ts, tpg_table2.te)
+	) x;
+
+-- Alignment with aggregation
+-- NB: Targetlist of outer query is *not* A_STAR...
+SELECT a, COUNT(a) FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+
+-- Equality qualifiers
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 ALIGN tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 ALIGN tpg_table4
+		ON tpg_table3.b = tpg_table4.d
+		WITH (tpg_table3.ts, tpg_table3.te, tpg_table4.ts, tpg_table4.te)
+	) x;
+
+-- Alignment with aggregation where targetlist of outer query is *not* A_STAR...
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT a, COUNT(a) FROM (
+	tpg_table3 ALIGN tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+
+-- Test relations with differently named temporal bound attributes and relation
+-- and column aliases.
+SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2 x(c,d,s,e)
+		ON b = d
+		WITH (ts, te, s, e)
+	) x;
+
+-- Range types for temporal boundaries, i.e., valid time columns
+SELECT * FROM (
+	tpg_table5 ALIGN tpg_table6
+		ON TRUE
+		WITH (t, t)
+	) x;
+
+--
+-- TEMPORAL ALIGNER: TEMPORAL JOIN EXAMPLE
+--
+
+-- Full temporal join example with absorbing where clause, timestamp
+-- propagation (see CTEs targetlists with V and U) and range types
+WITH t1 AS (SELECT *, t u FROM tpg_table5),
+	 t2 AS (SELECT *, t v FROM tpg_table6)
+SELECT t, b, x.a, y.a FROM (
+	t1 ALIGN t2
+		ON t1.b = t2.b
+		WITH (t, t)
+	) x
+	LEFT OUTER JOIN (
+		SELECT * FROM (
+		t2 ALIGN t1
+			ON t1.b = t2.b
+			WITH (t, t)
+		) y
+	) y
+	USING (b, t)
+	WHERE (
+			(lower(t) = lower(u) OR lower(t) = lower(v))
+			AND
+			(upper(t) = upper(u) OR upper(t) = upper(v))
+		)
+		OR u IS NULL
+		OR v IS NULL
+	ORDER BY 1,2,3,4;
+
+-- Full temporal join example with absorbing where clause, timestamp
+-- propagation (see CTEs targetlists with V and U) and scalar VALID TIME columns
+WITH t1 AS (SELECT *, ts us, te ue FROM tpg_table1),
+	 t2 AS (SELECT *, ts vs, te ve FROM tpg_table2)
+SELECT x.ts, x.te, b, a, c FROM (
+	t1 ALIGN t2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	LEFT OUTER JOIN (
+		SELECT * FROM (
+		t2 ALIGN t1
+			ON b = d
+			WITH (ts, te, ts, te)
+		) y
+	) y
+	ON b = d AND x.ts = y.ts AND x.te = y.te
+	WHERE (
+			(x.ts = us OR x.ts = vs)
+			AND
+			(x.te = ue OR x.te = ve)
+		)
+		OR us IS NULL
+		OR vs IS NULL
+	ORDER BY 1,2,3,4;
+
+-- Collation and varchar boundaries
+SELECT * FROM (
+	tpg_table7 x ALIGN tpg_table7 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Collation and varchar boundaries with incompatible collations (ERROR expected)
+SELECT * FROM (
+	tpg_table8 x ALIGN tpg_table8 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+
+--
+-- TEMPORAL ALIGNER: SELECTION PUSH-DOWN
+--
+
+-- VALID TIME columns are not safe to be pushed down, for the rest everything
+-- should work as usual.
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 ALIGN tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3;
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 ALIGN tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 AND ts > 3;
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 ALIGN tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 OR ts > 3;
+
+--
+-- TEMPORAL ALIGNER: DATA TYPES
+--
+
+-- Data types: Timestamps
+-- We use to_char here to be sure that we have the same output format on all
+-- platforms and locale configuration
+SELECT a, to_char(ts, 'YYYY-MM-DD') ts, to_char(te, 'YYYY-MM-DD') te FROM (
+	tpg_table9 t1 ALIGN tpg_table9 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Data types: Double precision
+SELECT a, ts, te FROM (
+	tpg_table10 t1 ALIGN tpg_table10 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Data types: Double precision with +/- infinity
+SELECT a, ts, te FROM (
+	tpg_table11 t1 ALIGN tpg_table11 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+
+
+--
+-- TEMPORAL NORMALIZER: BASICS
+--
+
+-- Equality qualifiers
+SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON tpg_table1.b = tpg_table2.d
+		WITH (tpg_table1.ts, tpg_table1.te, tpg_table2.ts, tpg_table2.te)
+	) x;
+
+-- Alignment with aggregation
+-- NB: Targetlist of outer query is *not* A_STAR...
+SELECT a, COUNT(a) FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+
+-- Equality qualifiers
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 NORMALIZE tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Equality qualifiers with FQN inside ON- and WITH-clause
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT * FROM (
+	tpg_table3 NORMALIZE tpg_table4
+		ON tpg_table3.b = tpg_table4.d
+		WITH (tpg_table3.ts, tpg_table3.te, tpg_table4.ts, tpg_table4.te)
+	) x;
+
+-- Alignment with aggregation where targetlist of outer query is *not* A_STAR...
+-- Test column positions where ts and te are not the last two columns.
+-- Please note: This was a restriction in an early implementation.
+SELECT a, COUNT(a) FROM (
+	tpg_table3 NORMALIZE tpg_table4
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x
+	GROUP BY a ORDER BY a;
+
+-- Test relations with differently named temporal bound attributes and relation
+-- and column aliases.
+SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2 x(c,d,s,e)
+		ON b = d
+		WITH (ts, te, s, e)
+	) x;
+
+-- Normalizer's USING clause (self-normalization)
+SELECT * FROM (
+	tpg_table1 t1 NORMALIZE tpg_table1 t2
+		USING (a)
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Range types for temporal boundaries, i.e., valid time columns
+SELECT * FROM (
+	tpg_table5 NORMALIZE tpg_table6
+		USING (b)
+		WITH (t, t)
+	) x;
+
+-- Collation and varchar boundaries
+SELECT * FROM (
+	tpg_table7 x NORMALIZE tpg_table7 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Collation and varchar boundaries with incompatible collations (ERROR expected)
+SELECT * FROM (
+	tpg_table8 x NORMALIZE tpg_table8 y
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x;
+
+--
+-- TEMPORAL NORMALIZER: SELECTION PUSH-DOWN
+--
+
+-- VALID TIME columns are not safe to be pushed down, for the rest everything
+-- should work as usual.
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 NORMALIZE tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3;
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 NORMALIZE tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 AND ts > 3;
+
+EXPLAIN (COSTS OFF) SELECT * FROM (
+	tpg_table2 NORMALIZE tpg_table1
+		ON TRUE
+		WITH (ts, te, ts, te)
+	) x
+	WHERE c < 3 OR ts > 3;
+
+--
+-- TEMPORAL NORMALIZER: DATA TYPES
+--
+
+-- Data types: Timestamps
+-- We use to_char here to be sure that we have the same output format on all
+-- platforms and locale configuration
+SELECT a, to_char(ts, 'YYYY-MM-DD') ts, to_char(te, 'YYYY-MM-DD') te FROM (
+	tpg_table9 t1 NORMALIZE tpg_table9 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Data types: Double precision
+SELECT a, ts, te FROM (
+	tpg_table10 t1 NORMALIZE tpg_table10 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+
+-- Data types: Double precision with +/- infinity
+SELECT a, ts, te FROM (
+	tpg_table11 t1 NORMALIZE tpg_table11 t2
+		ON t1.a = 0
+		WITH (ts, te, ts, te)
+	) x;
+
+--
+-- TEMPORAL ALIGNER AND NORMALIZER: VIEWS
+--
+
+-- Views with temporal normalization
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 NORMALIZE tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+
+TABLE v;
+DROP VIEW v;
+
+-- Views with temporal alignment
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 ALIGN tpg_table2
+		ON b = d
+		WITH (ts, te, ts, te)
+	) x;
+
+TABLE v;
+DROP VIEW v;
+
+-- Testing temporal normalization with ambiguous columns, i.e. columns that
+-- are used internally...
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 AS r(p1, p1_0, "p1_-1", p1_1) NORMALIZE tpg_table2 s
+		ON r.p1_0 = s.d
+		WITH ("p1_-1", p1_1, ts, te)
+	) x;
+
+TABLE v;
+DROP VIEW v;
+
+-- Testing temporal alignment with ambiguous columns, i.e. columns that
+-- are used internally...
+CREATE TEMP VIEW v AS SELECT * FROM (
+	tpg_table1 AS r(p1, p1_0, "p1_-1", p1_1) ALIGN tpg_table2 s
+		ON r.p1_0 = s.d
+		WITH ("p1_-1",p1_1,ts,te)
+	) x;
+
+TABLE v;
+DROP VIEW v;
+
+
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to