diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/horology.out /tmp/cirrus-ci-build/src/test/regress/results/horology.out --- /tmp/cirrus-ci-build/src/test/regress/expected/horology.out 2026-03-09 20:33:45.160311140 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/horology.out 2026-03-09 20:38:46.792091014 +0000 @@ -1351,2540 +1351,8 @@ FROM TIME_TBL t, INTERVAL_TBL i WHERE isfinite(i.f1) ORDER BY 1,2; - t | i | add | subtract --------------+-------------------------------+-------------+------------- - 00:00:00 | @ 14 secs ago | 23:59:46 | 00:00:14 - 00:00:00 | @ 1 min | 00:01:00 | 23:59:00 - 00:00:00 | @ 5 hours | 05:00:00 | 19:00:00 - 00:00:00 | @ 1 day 2 hours 3 mins 4 secs | 02:03:04 | 21:56:56 - 00:00:00 | @ 10 days | 00:00:00 | 00:00:00 - 00:00:00 | @ 3 mons | 00:00:00 | 00:00:00 - 00:00:00 | @ 5 mons | 00:00:00 | 00:00:00 - 00:00:00 | @ 5 mons 12 hours | 12:00:00 | 12:00:00 - 00:00:00 | @ 6 years | 00:00:00 | 00:00:00 - 00:00:00 | @ 34 years | 00:00:00 | 00:00:00 - 01:00:00 | @ 14 secs ago | 00:59:46 | 01:00:14 - 01:00:00 | @ 1 min | 01:01:00 | 00:59:00 - 01:00:00 | @ 5 hours | 06:00:00 | 20:00:00 - 01:00:00 | @ 1 day 2 hours 3 mins 4 secs | 03:03:04 | 22:56:56 - 01:00:00 | @ 10 days | 01:00:00 | 01:00:00 - 01:00:00 | @ 3 mons | 01:00:00 | 01:00:00 - 01:00:00 | @ 5 mons | 01:00:00 | 01:00:00 - 01:00:00 | @ 5 mons 12 hours | 13:00:00 | 13:00:00 - 01:00:00 | @ 6 years | 01:00:00 | 01:00:00 - 01:00:00 | @ 34 years | 01:00:00 | 01:00:00 - 02:03:00 | @ 14 secs ago | 02:02:46 | 02:03:14 - 02:03:00 | @ 1 min | 02:04:00 | 02:02:00 - 02:03:00 | @ 5 hours | 07:03:00 | 21:03:00 - 02:03:00 | @ 1 day 2 hours 3 mins 4 secs | 04:06:04 | 23:59:56 - 02:03:00 | @ 10 days | 02:03:00 | 02:03:00 - 02:03:00 | @ 3 mons | 02:03:00 | 02:03:00 - 02:03:00 | @ 5 mons | 02:03:00 | 02:03:00 - 02:03:00 | @ 5 mons 12 hours | 14:03:00 | 14:03:00 - 02:03:00 | @ 6 years | 02:03:00 | 02:03:00 - 02:03:00 | @ 34 years | 02:03:00 | 02:03:00 - 11:59:00 | @ 14 secs ago | 11:58:46 | 11:59:14 - 11:59:00 | @ 1 min | 12:00:00 | 11:58:00 - 11:59:00 | @ 5 hours | 16:59:00 | 06:59:00 - 11:59:00 | @ 1 day 2 hours 3 mins 4 secs | 14:02:04 | 09:55:56 - 11:59:00 | @ 10 days | 11:59:00 | 11:59:00 - 11:59:00 | @ 3 mons | 11:59:00 | 11:59:00 - 11:59:00 | @ 5 mons | 11:59:00 | 11:59:00 - 11:59:00 | @ 5 mons 12 hours | 23:59:00 | 23:59:00 - 11:59:00 | @ 6 years | 11:59:00 | 11:59:00 - 11:59:00 | @ 34 years | 11:59:00 | 11:59:00 - 12:00:00 | @ 14 secs ago | 11:59:46 | 12:00:14 - 12:00:00 | @ 1 min | 12:01:00 | 11:59:00 - 12:00:00 | @ 5 hours | 17:00:00 | 07:00:00 - 12:00:00 | @ 1 day 2 hours 3 mins 4 secs | 14:03:04 | 09:56:56 - 12:00:00 | @ 10 days | 12:00:00 | 12:00:00 - 12:00:00 | @ 3 mons | 12:00:00 | 12:00:00 - 12:00:00 | @ 5 mons | 12:00:00 | 12:00:00 - 12:00:00 | @ 5 mons 12 hours | 00:00:00 | 00:00:00 - 12:00:00 | @ 6 years | 12:00:00 | 12:00:00 - 12:00:00 | @ 34 years | 12:00:00 | 12:00:00 - 12:01:00 | @ 14 secs ago | 12:00:46 | 12:01:14 - 12:01:00 | @ 1 min | 12:02:00 | 12:00:00 - 12:01:00 | @ 5 hours | 17:01:00 | 07:01:00 - 12:01:00 | @ 1 day 2 hours 3 mins 4 secs | 14:04:04 | 09:57:56 - 12:01:00 | @ 10 days | 12:01:00 | 12:01:00 - 12:01:00 | @ 3 mons | 12:01:00 | 12:01:00 - 12:01:00 | @ 5 mons | 12:01:00 | 12:01:00 - 12:01:00 | @ 5 mons 12 hours | 00:01:00 | 00:01:00 - 12:01:00 | @ 6 years | 12:01:00 | 12:01:00 - 12:01:00 | @ 34 years | 12:01:00 | 12:01:00 - 15:36:39 | @ 14 secs ago | 15:36:25 | 15:36:53 - 15:36:39 | @ 14 secs ago | 15:36:25 | 15:36:53 - 15:36:39 | @ 1 min | 15:37:39 | 15:35:39 - 15:36:39 | @ 1 min | 15:37:39 | 15:35:39 - 15:36:39 | @ 5 hours | 20:36:39 | 10:36:39 - 15:36:39 | @ 5 hours | 20:36:39 | 10:36:39 - 15:36:39 | @ 1 day 2 hours 3 mins 4 secs | 17:39:43 | 13:33:35 - 15:36:39 | @ 1 day 2 hours 3 mins 4 secs | 17:39:43 | 13:33:35 - 15:36:39 | @ 10 days | 15:36:39 | 15:36:39 - 15:36:39 | @ 10 days | 15:36:39 | 15:36:39 - 15:36:39 | @ 3 mons | 15:36:39 | 15:36:39 - 15:36:39 | @ 3 mons | 15:36:39 | 15:36:39 - 15:36:39 | @ 5 mons | 15:36:39 | 15:36:39 - 15:36:39 | @ 5 mons | 15:36:39 | 15:36:39 - 15:36:39 | @ 5 mons 12 hours | 03:36:39 | 03:36:39 - 15:36:39 | @ 5 mons 12 hours | 03:36:39 | 03:36:39 - 15:36:39 | @ 6 years | 15:36:39 | 15:36:39 - 15:36:39 | @ 6 years | 15:36:39 | 15:36:39 - 15:36:39 | @ 34 years | 15:36:39 | 15:36:39 - 15:36:39 | @ 34 years | 15:36:39 | 15:36:39 - 23:59:00 | @ 14 secs ago | 23:58:46 | 23:59:14 - 23:59:00 | @ 1 min | 00:00:00 | 23:58:00 - 23:59:00 | @ 5 hours | 04:59:00 | 18:59:00 - 23:59:00 | @ 1 day 2 hours 3 mins 4 secs | 02:02:04 | 21:55:56 - 23:59:00 | @ 10 days | 23:59:00 | 23:59:00 - 23:59:00 | @ 3 mons | 23:59:00 | 23:59:00 - 23:59:00 | @ 5 mons | 23:59:00 | 23:59:00 - 23:59:00 | @ 5 mons 12 hours | 11:59:00 | 11:59:00 - 23:59:00 | @ 6 years | 23:59:00 | 23:59:00 - 23:59:00 | @ 34 years | 23:59:00 | 23:59:00 - 23:59:59.99 | @ 14 secs ago | 23:59:45.99 | 00:00:13.99 - 23:59:59.99 | @ 1 min | 00:00:59.99 | 23:58:59.99 - 23:59:59.99 | @ 5 hours | 04:59:59.99 | 18:59:59.99 - 23:59:59.99 | @ 1 day 2 hours 3 mins 4 secs | 02:03:03.99 | 21:56:55.99 - 23:59:59.99 | @ 10 days | 23:59:59.99 | 23:59:59.99 - 23:59:59.99 | @ 3 mons | 23:59:59.99 | 23:59:59.99 - 23:59:59.99 | @ 5 mons | 23:59:59.99 | 23:59:59.99 - 23:59:59.99 | @ 5 mons 12 hours | 11:59:59.99 | 11:59:59.99 - 23:59:59.99 | @ 6 years | 23:59:59.99 | 23:59:59.99 - 23:59:59.99 | @ 34 years | 23:59:59.99 | 23:59:59.99 -(100 rows) - -SELECT t.f1 AS t, i.f1 AS i, t.f1 + i.f1 AS "add", t.f1 - i.f1 AS "subtract" - FROM TIMETZ_TBL t, INTERVAL_TBL i - WHERE isfinite(i.f1) - ORDER BY 1,2; - t | i | add | subtract -----------------+-------------------------------+----------------+---------------- - 00:01:00-07 | @ 14 secs ago | 00:00:46-07 | 00:01:14-07 - 00:01:00-07 | @ 1 min | 00:02:00-07 | 00:00:00-07 - 00:01:00-07 | @ 5 hours | 05:01:00-07 | 19:01:00-07 - 00:01:00-07 | @ 1 day 2 hours 3 mins 4 secs | 02:04:04-07 | 21:57:56-07 - 00:01:00-07 | @ 10 days | 00:01:00-07 | 00:01:00-07 - 00:01:00-07 | @ 3 mons | 00:01:00-07 | 00:01:00-07 - 00:01:00-07 | @ 5 mons | 00:01:00-07 | 00:01:00-07 - 00:01:00-07 | @ 5 mons 12 hours | 12:01:00-07 | 12:01:00-07 - 00:01:00-07 | @ 6 years | 00:01:00-07 | 00:01:00-07 - 00:01:00-07 | @ 34 years | 00:01:00-07 | 00:01:00-07 - 01:00:00-07 | @ 14 secs ago | 00:59:46-07 | 01:00:14-07 - 01:00:00-07 | @ 1 min | 01:01:00-07 | 00:59:00-07 - 01:00:00-07 | @ 5 hours | 06:00:00-07 | 20:00:00-07 - 01:00:00-07 | @ 1 day 2 hours 3 mins 4 secs | 03:03:04-07 | 22:56:56-07 - 01:00:00-07 | @ 10 days | 01:00:00-07 | 01:00:00-07 - 01:00:00-07 | @ 3 mons | 01:00:00-07 | 01:00:00-07 - 01:00:00-07 | @ 5 mons | 01:00:00-07 | 01:00:00-07 - 01:00:00-07 | @ 5 mons 12 hours | 13:00:00-07 | 13:00:00-07 - 01:00:00-07 | @ 6 years | 01:00:00-07 | 01:00:00-07 - 01:00:00-07 | @ 34 years | 01:00:00-07 | 01:00:00-07 - 02:03:00-07 | @ 14 secs ago | 02:02:46-07 | 02:03:14-07 - 02:03:00-07 | @ 1 min | 02:04:00-07 | 02:02:00-07 - 02:03:00-07 | @ 5 hours | 07:03:00-07 | 21:03:00-07 - 02:03:00-07 | @ 1 day 2 hours 3 mins 4 secs | 04:06:04-07 | 23:59:56-07 - 02:03:00-07 | @ 10 days | 02:03:00-07 | 02:03:00-07 - 02:03:00-07 | @ 3 mons | 02:03:00-07 | 02:03:00-07 - 02:03:00-07 | @ 5 mons | 02:03:00-07 | 02:03:00-07 - 02:03:00-07 | @ 5 mons 12 hours | 14:03:00-07 | 14:03:00-07 - 02:03:00-07 | @ 6 years | 02:03:00-07 | 02:03:00-07 - 02:03:00-07 | @ 34 years | 02:03:00-07 | 02:03:00-07 - 08:08:00-04 | @ 14 secs ago | 08:07:46-04 | 08:08:14-04 - 08:08:00-04 | @ 1 min | 08:09:00-04 | 08:07:00-04 - 08:08:00-04 | @ 5 hours | 13:08:00-04 | 03:08:00-04 - 08:08:00-04 | @ 1 day 2 hours 3 mins 4 secs | 10:11:04-04 | 06:04:56-04 - 08:08:00-04 | @ 10 days | 08:08:00-04 | 08:08:00-04 - 08:08:00-04 | @ 3 mons | 08:08:00-04 | 08:08:00-04 - 08:08:00-04 | @ 5 mons | 08:08:00-04 | 08:08:00-04 - 08:08:00-04 | @ 5 mons 12 hours | 20:08:00-04 | 20:08:00-04 - 08:08:00-04 | @ 6 years | 08:08:00-04 | 08:08:00-04 - 08:08:00-04 | @ 34 years | 08:08:00-04 | 08:08:00-04 - 07:07:00-08 | @ 14 secs ago | 07:06:46-08 | 07:07:14-08 - 07:07:00-08 | @ 1 min | 07:08:00-08 | 07:06:00-08 - 07:07:00-08 | @ 5 hours | 12:07:00-08 | 02:07:00-08 - 07:07:00-08 | @ 1 day 2 hours 3 mins 4 secs | 09:10:04-08 | 05:03:56-08 - 07:07:00-08 | @ 10 days | 07:07:00-08 | 07:07:00-08 - 07:07:00-08 | @ 3 mons | 07:07:00-08 | 07:07:00-08 - 07:07:00-08 | @ 5 mons | 07:07:00-08 | 07:07:00-08 - 07:07:00-08 | @ 5 mons 12 hours | 19:07:00-08 | 19:07:00-08 - 07:07:00-08 | @ 6 years | 07:07:00-08 | 07:07:00-08 - 07:07:00-08 | @ 34 years | 07:07:00-08 | 07:07:00-08 - 11:59:00-07 | @ 14 secs ago | 11:58:46-07 | 11:59:14-07 - 11:59:00-07 | @ 1 min | 12:00:00-07 | 11:58:00-07 - 11:59:00-07 | @ 5 hours | 16:59:00-07 | 06:59:00-07 - 11:59:00-07 | @ 1 day 2 hours 3 mins 4 secs | 14:02:04-07 | 09:55:56-07 - 11:59:00-07 | @ 10 days | 11:59:00-07 | 11:59:00-07 - 11:59:00-07 | @ 3 mons | 11:59:00-07 | 11:59:00-07 - 11:59:00-07 | @ 5 mons | 11:59:00-07 | 11:59:00-07 - 11:59:00-07 | @ 5 mons 12 hours | 23:59:00-07 | 23:59:00-07 - 11:59:00-07 | @ 6 years | 11:59:00-07 | 11:59:00-07 - 11:59:00-07 | @ 34 years | 11:59:00-07 | 11:59:00-07 - 12:00:00-07 | @ 14 secs ago | 11:59:46-07 | 12:00:14-07 - 12:00:00-07 | @ 1 min | 12:01:00-07 | 11:59:00-07 - 12:00:00-07 | @ 5 hours | 17:00:00-07 | 07:00:00-07 - 12:00:00-07 | @ 1 day 2 hours 3 mins 4 secs | 14:03:04-07 | 09:56:56-07 - 12:00:00-07 | @ 10 days | 12:00:00-07 | 12:00:00-07 - 12:00:00-07 | @ 3 mons | 12:00:00-07 | 12:00:00-07 - 12:00:00-07 | @ 5 mons | 12:00:00-07 | 12:00:00-07 - 12:00:00-07 | @ 5 mons 12 hours | 00:00:00-07 | 00:00:00-07 - 12:00:00-07 | @ 6 years | 12:00:00-07 | 12:00:00-07 - 12:00:00-07 | @ 34 years | 12:00:00-07 | 12:00:00-07 - 12:01:00-07 | @ 14 secs ago | 12:00:46-07 | 12:01:14-07 - 12:01:00-07 | @ 1 min | 12:02:00-07 | 12:00:00-07 - 12:01:00-07 | @ 5 hours | 17:01:00-07 | 07:01:00-07 - 12:01:00-07 | @ 1 day 2 hours 3 mins 4 secs | 14:04:04-07 | 09:57:56-07 - 12:01:00-07 | @ 10 days | 12:01:00-07 | 12:01:00-07 - 12:01:00-07 | @ 3 mons | 12:01:00-07 | 12:01:00-07 - 12:01:00-07 | @ 5 mons | 12:01:00-07 | 12:01:00-07 - 12:01:00-07 | @ 5 mons 12 hours | 00:01:00-07 | 00:01:00-07 - 12:01:00-07 | @ 6 years | 12:01:00-07 | 12:01:00-07 - 12:01:00-07 | @ 34 years | 12:01:00-07 | 12:01:00-07 - 15:36:39-04 | @ 14 secs ago | 15:36:25-04 | 15:36:53-04 - 15:36:39-04 | @ 1 min | 15:37:39-04 | 15:35:39-04 - 15:36:39-04 | @ 5 hours | 20:36:39-04 | 10:36:39-04 - 15:36:39-04 | @ 1 day 2 hours 3 mins 4 secs | 17:39:43-04 | 13:33:35-04 - 15:36:39-04 | @ 10 days | 15:36:39-04 | 15:36:39-04 - 15:36:39-04 | @ 3 mons | 15:36:39-04 | 15:36:39-04 - 15:36:39-04 | @ 5 mons | 15:36:39-04 | 15:36:39-04 - 15:36:39-04 | @ 5 mons 12 hours | 03:36:39-04 | 03:36:39-04 - 15:36:39-04 | @ 6 years | 15:36:39-04 | 15:36:39-04 - 15:36:39-04 | @ 34 years | 15:36:39-04 | 15:36:39-04 - 15:36:39-05 | @ 14 secs ago | 15:36:25-05 | 15:36:53-05 - 15:36:39-05 | @ 1 min | 15:37:39-05 | 15:35:39-05 - 15:36:39-05 | @ 5 hours | 20:36:39-05 | 10:36:39-05 - 15:36:39-05 | @ 1 day 2 hours 3 mins 4 secs | 17:39:43-05 | 13:33:35-05 - 15:36:39-05 | @ 10 days | 15:36:39-05 | 15:36:39-05 - 15:36:39-05 | @ 3 mons | 15:36:39-05 | 15:36:39-05 - 15:36:39-05 | @ 5 mons | 15:36:39-05 | 15:36:39-05 - 15:36:39-05 | @ 5 mons 12 hours | 03:36:39-05 | 03:36:39-05 - 15:36:39-05 | @ 6 years | 15:36:39-05 | 15:36:39-05 - 15:36:39-05 | @ 34 years | 15:36:39-05 | 15:36:39-05 - 23:59:00-07 | @ 14 secs ago | 23:58:46-07 | 23:59:14-07 - 23:59:00-07 | @ 1 min | 00:00:00-07 | 23:58:00-07 - 23:59:00-07 | @ 5 hours | 04:59:00-07 | 18:59:00-07 - 23:59:00-07 | @ 1 day 2 hours 3 mins 4 secs | 02:02:04-07 | 21:55:56-07 - 23:59:00-07 | @ 10 days | 23:59:00-07 | 23:59:00-07 - 23:59:00-07 | @ 3 mons | 23:59:00-07 | 23:59:00-07 - 23:59:00-07 | @ 5 mons | 23:59:00-07 | 23:59:00-07 - 23:59:00-07 | @ 5 mons 12 hours | 11:59:00-07 | 11:59:00-07 - 23:59:00-07 | @ 6 years | 23:59:00-07 | 23:59:00-07 - 23:59:00-07 | @ 34 years | 23:59:00-07 | 23:59:00-07 - 23:59:59.99-07 | @ 14 secs ago | 23:59:45.99-07 | 00:00:13.99-07 - 23:59:59.99-07 | @ 1 min | 00:00:59.99-07 | 23:58:59.99-07 - 23:59:59.99-07 | @ 5 hours | 04:59:59.99-07 | 18:59:59.99-07 - 23:59:59.99-07 | @ 1 day 2 hours 3 mins 4 secs | 02:03:03.99-07 | 21:56:55.99-07 - 23:59:59.99-07 | @ 10 days | 23:59:59.99-07 | 23:59:59.99-07 - 23:59:59.99-07 | @ 3 mons | 23:59:59.99-07 | 23:59:59.99-07 - 23:59:59.99-07 | @ 5 mons | 23:59:59.99-07 | 23:59:59.99-07 - 23:59:59.99-07 | @ 5 mons 12 hours | 11:59:59.99-07 | 11:59:59.99-07 - 23:59:59.99-07 | @ 6 years | 23:59:59.99-07 | 23:59:59.99-07 - 23:59:59.99-07 | @ 34 years | 23:59:59.99-07 | 23:59:59.99-07 -(120 rows) - --- SQL9x OVERLAPS operator --- test with time zone -SELECT (timestamp with time zone '2000-11-27', timestamp with time zone '2000-11-28') - OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "True"; - True ------- - t -(1 row) - -SELECT (timestamp with time zone '2000-11-26', timestamp with time zone '2000-11-27') - OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "False"; - False -------- - f -(1 row) - -SELECT (timestamp with time zone '2000-11-27', timestamp with time zone '2000-11-28') - OVERLAPS (timestamp with time zone '2000-11-27 12:00', interval '1 day') AS "True"; - True ------- - t -(1 row) - -SELECT (timestamp with time zone '2000-11-27', interval '12 hours') - OVERLAPS (timestamp with time zone '2000-11-27 12:00', timestamp with time zone '2000-11-30') AS "False"; - False -------- - f -(1 row) - -SELECT (timestamp with time zone '2000-11-27', interval '12 hours') - OVERLAPS (timestamp with time zone '2000-11-27', interval '12 hours') AS "True"; - True ------- - t -(1 row) - -SELECT (timestamp with time zone '2000-11-27', interval '12 hours') - OVERLAPS (timestamp with time zone '2000-11-27 12:00', interval '12 hours') AS "False"; - False -------- - f -(1 row) - --- test without time zone -SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') - OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "True"; - True ------- - t -(1 row) - -SELECT (timestamp without time zone '2000-11-26', timestamp without time zone '2000-11-27') - OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; - False -------- - f -(1 row) - -SELECT (timestamp without time zone '2000-11-27', timestamp without time zone '2000-11-28') - OVERLAPS (timestamp without time zone '2000-11-27 12:00', interval '1 day') AS "True"; - True ------- - t -(1 row) - -SELECT (timestamp without time zone '2000-11-27', interval '12 hours') - OVERLAPS (timestamp without time zone '2000-11-27 12:00', timestamp without time zone '2000-11-30') AS "False"; - False -------- - f -(1 row) - -SELECT (timestamp without time zone '2000-11-27', interval '12 hours') - OVERLAPS (timestamp without time zone '2000-11-27', interval '12 hours') AS "True"; - True ------- - t -(1 row) - -SELECT (timestamp without time zone '2000-11-27', interval '12 hours') - OVERLAPS (timestamp without time zone '2000-11-27 12:00', interval '12 hours') AS "False"; - False -------- - f -(1 row) - --- test time and interval -SELECT (time '00:00', time '01:00') - OVERLAPS (time '00:30', time '01:30') AS "True"; - True ------- - t -(1 row) - -SELECT (time '00:00', interval '1 hour') - OVERLAPS (time '00:30', interval '1 hour') AS "True"; - True ------- - t -(1 row) - -SELECT (time '00:00', interval '1 hour') - OVERLAPS (time '01:30', interval '1 hour') AS "False"; - False -------- - f -(1 row) - --- SQL99 seems to want this to be false (and we conform to the spec). --- istm that this *should* return true, on the theory that time --- intervals can wrap around the day boundary - thomas 2001-09-25 -SELECT (time '00:00', interval '1 hour') - OVERLAPS (time '01:30', interval '1 day') AS "False"; - False -------- - f -(1 row) - -CREATE TABLE TEMP_TIMESTAMP (f1 timestamp with time zone); --- get some candidate input values -INSERT INTO TEMP_TIMESTAMP (f1) - SELECT d1 FROM TIMESTAMP_TBL - WHERE d1 BETWEEN '13-jun-1957' AND '1-jan-1997' - OR d1 BETWEEN '1-jan-1999' AND '1-jan-2010'; -SELECT f1 AS "timestamp" - FROM TEMP_TIMESTAMP - ORDER BY "timestamp"; - timestamp ------------------------------- - Thu Jan 01 00:00:00 1970 PST - Wed Feb 28 17:32:01 1996 PST - Thu Feb 29 17:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST - Mon Dec 30 17:32:01 1996 PST - Tue Dec 31 17:32:01 1996 PST - Fri Dec 31 17:32:01 1999 PST - Sat Jan 01 17:32:01 2000 PST - Wed Mar 15 02:14:05 2000 PST - Wed Mar 15 03:14:04 2000 PST - Wed Mar 15 08:14:01 2000 PST - Wed Mar 15 12:14:03 2000 PST - Wed Mar 15 13:14:02 2000 PST - Sun Dec 31 17:32:01 2000 PST - Mon Jan 01 17:32:01 2001 PST - Sat Sep 22 18:19:20 2001 PDT -(16 rows) - -SELECT d.f1 AS "timestamp", t.f1 AS "interval", d.f1 + t.f1 AS plus - FROM TEMP_TIMESTAMP d, INTERVAL_TBL t - ORDER BY plus, "timestamp", "interval"; - timestamp | interval | plus -------------------------------+-------------------------------+------------------------------ - Thu Jan 01 00:00:00 1970 PST | -infinity | -infinity - Wed Feb 28 17:32:01 1996 PST | -infinity | -infinity - Thu Feb 29 17:32:01 1996 PST | -infinity | -infinity - Fri Mar 01 17:32:01 1996 PST | -infinity | -infinity - Mon Dec 30 17:32:01 1996 PST | -infinity | -infinity - Tue Dec 31 17:32:01 1996 PST | -infinity | -infinity - Fri Dec 31 17:32:01 1999 PST | -infinity | -infinity - Sat Jan 01 17:32:01 2000 PST | -infinity | -infinity - Wed Mar 15 02:14:05 2000 PST | -infinity | -infinity - Wed Mar 15 03:14:04 2000 PST | -infinity | -infinity - Wed Mar 15 08:14:01 2000 PST | -infinity | -infinity - Wed Mar 15 12:14:03 2000 PST | -infinity | -infinity - Wed Mar 15 13:14:02 2000 PST | -infinity | -infinity - Sun Dec 31 17:32:01 2000 PST | -infinity | -infinity - Mon Jan 01 17:32:01 2001 PST | -infinity | -infinity - Sat Sep 22 18:19:20 2001 PDT | -infinity | -infinity - Thu Jan 01 00:00:00 1970 PST | @ 14 secs ago | Wed Dec 31 23:59:46 1969 PST - Thu Jan 01 00:00:00 1970 PST | @ 1 min | Thu Jan 01 00:01:00 1970 PST - Thu Jan 01 00:00:00 1970 PST | @ 5 hours | Thu Jan 01 05:00:00 1970 PST - Thu Jan 01 00:00:00 1970 PST | @ 1 day 2 hours 3 mins 4 secs | Fri Jan 02 02:03:04 1970 PST - Thu Jan 01 00:00:00 1970 PST | @ 10 days | Sun Jan 11 00:00:00 1970 PST - Thu Jan 01 00:00:00 1970 PST | @ 3 mons | Wed Apr 01 00:00:00 1970 PST - Thu Jan 01 00:00:00 1970 PST | @ 5 mons | Mon Jun 01 00:00:00 1970 PDT - Thu Jan 01 00:00:00 1970 PST | @ 5 mons 12 hours | Mon Jun 01 12:00:00 1970 PDT - Thu Jan 01 00:00:00 1970 PST | @ 6 years | Thu Jan 01 00:00:00 1976 PST - Wed Feb 28 17:32:01 1996 PST | @ 14 secs ago | Wed Feb 28 17:31:47 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 1 min | Wed Feb 28 17:33:01 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 5 hours | Wed Feb 28 22:32:01 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 14 secs ago | Thu Feb 29 17:31:47 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 1 min | Thu Feb 29 17:33:01 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Feb 29 19:35:05 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 5 hours | Thu Feb 29 22:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 14 secs ago | Fri Mar 01 17:31:47 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 1 min | Fri Mar 01 17:33:01 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Fri Mar 01 19:35:05 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 5 hours | Fri Mar 01 22:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Sat Mar 02 19:35:05 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 10 days | Sat Mar 09 17:32:01 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 10 days | Sun Mar 10 17:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 10 days | Mon Mar 11 17:32:01 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 3 mons | Tue May 28 17:32:01 1996 PDT - Thu Feb 29 17:32:01 1996 PST | @ 3 mons | Wed May 29 17:32:01 1996 PDT - Fri Mar 01 17:32:01 1996 PST | @ 3 mons | Sat Jun 01 17:32:01 1996 PDT - Wed Feb 28 17:32:01 1996 PST | @ 5 mons | Sun Jul 28 17:32:01 1996 PDT - Wed Feb 28 17:32:01 1996 PST | @ 5 mons 12 hours | Mon Jul 29 05:32:01 1996 PDT - Thu Feb 29 17:32:01 1996 PST | @ 5 mons | Mon Jul 29 17:32:01 1996 PDT - Thu Feb 29 17:32:01 1996 PST | @ 5 mons 12 hours | Tue Jul 30 05:32:01 1996 PDT - Fri Mar 01 17:32:01 1996 PST | @ 5 mons | Thu Aug 01 17:32:01 1996 PDT - Fri Mar 01 17:32:01 1996 PST | @ 5 mons 12 hours | Fri Aug 02 05:32:01 1996 PDT - Mon Dec 30 17:32:01 1996 PST | @ 14 secs ago | Mon Dec 30 17:31:47 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 1 min | Mon Dec 30 17:33:01 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 5 hours | Mon Dec 30 22:32:01 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 14 secs ago | Tue Dec 31 17:31:47 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 1 min | Tue Dec 31 17:33:01 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Dec 31 19:35:05 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 5 hours | Tue Dec 31 22:32:01 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Wed Jan 01 19:35:05 1997 PST - Mon Dec 30 17:32:01 1996 PST | @ 10 days | Thu Jan 09 17:32:01 1997 PST - Tue Dec 31 17:32:01 1996 PST | @ 10 days | Fri Jan 10 17:32:01 1997 PST - Mon Dec 30 17:32:01 1996 PST | @ 3 mons | Sun Mar 30 17:32:01 1997 PST - Tue Dec 31 17:32:01 1996 PST | @ 3 mons | Mon Mar 31 17:32:01 1997 PST - Mon Dec 30 17:32:01 1996 PST | @ 5 mons | Fri May 30 17:32:01 1997 PDT - Mon Dec 30 17:32:01 1996 PST | @ 5 mons 12 hours | Sat May 31 05:32:01 1997 PDT - Tue Dec 31 17:32:01 1996 PST | @ 5 mons | Sat May 31 17:32:01 1997 PDT - Tue Dec 31 17:32:01 1996 PST | @ 5 mons 12 hours | Sun Jun 01 05:32:01 1997 PDT - Fri Dec 31 17:32:01 1999 PST | @ 14 secs ago | Fri Dec 31 17:31:47 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 1 min | Fri Dec 31 17:33:01 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 5 hours | Fri Dec 31 22:32:01 1999 PST - Sat Jan 01 17:32:01 2000 PST | @ 14 secs ago | Sat Jan 01 17:31:47 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 1 min | Sat Jan 01 17:33:01 2000 PST - Fri Dec 31 17:32:01 1999 PST | @ 1 day 2 hours 3 mins 4 secs | Sat Jan 01 19:35:05 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 5 hours | Sat Jan 01 22:32:01 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Sun Jan 02 19:35:05 2000 PST - Fri Dec 31 17:32:01 1999 PST | @ 10 days | Mon Jan 10 17:32:01 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 10 days | Tue Jan 11 17:32:01 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 14 secs ago | Wed Mar 15 02:13:51 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 1 min | Wed Mar 15 02:15:05 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 14 secs ago | Wed Mar 15 03:13:50 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 1 min | Wed Mar 15 03:15:04 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 5 hours | Wed Mar 15 07:14:05 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 14 secs ago | Wed Mar 15 08:13:47 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 5 hours | Wed Mar 15 08:14:04 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 1 min | Wed Mar 15 08:15:01 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 14 secs ago | Wed Mar 15 12:13:49 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 1 min | Wed Mar 15 12:15:03 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 14 secs ago | Wed Mar 15 13:13:48 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 5 hours | Wed Mar 15 13:14:01 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 1 min | Wed Mar 15 13:15:02 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 5 hours | Wed Mar 15 17:14:03 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 5 hours | Wed Mar 15 18:14:02 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Mar 16 04:17:09 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Mar 16 05:17:08 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Mar 16 10:17:05 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Mar 16 14:17:07 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Mar 16 15:17:06 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 10 days | Sat Mar 25 02:14:05 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 10 days | Sat Mar 25 03:14:04 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 10 days | Sat Mar 25 08:14:01 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 10 days | Sat Mar 25 12:14:03 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 10 days | Sat Mar 25 13:14:02 2000 PST - Fri Dec 31 17:32:01 1999 PST | @ 3 mons | Fri Mar 31 17:32:01 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 3 mons | Sat Apr 01 17:32:01 2000 PST - Fri Dec 31 17:32:01 1999 PST | @ 5 mons | Wed May 31 17:32:01 2000 PDT - Fri Dec 31 17:32:01 1999 PST | @ 5 mons 12 hours | Thu Jun 01 05:32:01 2000 PDT - Sat Jan 01 17:32:01 2000 PST | @ 5 mons | Thu Jun 01 17:32:01 2000 PDT - Sat Jan 01 17:32:01 2000 PST | @ 5 mons 12 hours | Fri Jun 02 05:32:01 2000 PDT - Wed Mar 15 02:14:05 2000 PST | @ 3 mons | Thu Jun 15 02:14:05 2000 PDT - Wed Mar 15 03:14:04 2000 PST | @ 3 mons | Thu Jun 15 03:14:04 2000 PDT - Wed Mar 15 08:14:01 2000 PST | @ 3 mons | Thu Jun 15 08:14:01 2000 PDT - Wed Mar 15 12:14:03 2000 PST | @ 3 mons | Thu Jun 15 12:14:03 2000 PDT - Wed Mar 15 13:14:02 2000 PST | @ 3 mons | Thu Jun 15 13:14:02 2000 PDT - Wed Mar 15 02:14:05 2000 PST | @ 5 mons | Tue Aug 15 02:14:05 2000 PDT - Wed Mar 15 03:14:04 2000 PST | @ 5 mons | Tue Aug 15 03:14:04 2000 PDT - Wed Mar 15 08:14:01 2000 PST | @ 5 mons | Tue Aug 15 08:14:01 2000 PDT - Wed Mar 15 12:14:03 2000 PST | @ 5 mons | Tue Aug 15 12:14:03 2000 PDT - Wed Mar 15 13:14:02 2000 PST | @ 5 mons | Tue Aug 15 13:14:02 2000 PDT - Wed Mar 15 02:14:05 2000 PST | @ 5 mons 12 hours | Tue Aug 15 14:14:05 2000 PDT - Wed Mar 15 03:14:04 2000 PST | @ 5 mons 12 hours | Tue Aug 15 15:14:04 2000 PDT - Wed Mar 15 08:14:01 2000 PST | @ 5 mons 12 hours | Tue Aug 15 20:14:01 2000 PDT - Wed Mar 15 12:14:03 2000 PST | @ 5 mons 12 hours | Wed Aug 16 00:14:03 2000 PDT - Wed Mar 15 13:14:02 2000 PST | @ 5 mons 12 hours | Wed Aug 16 01:14:02 2000 PDT - Sun Dec 31 17:32:01 2000 PST | @ 14 secs ago | Sun Dec 31 17:31:47 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 1 min | Sun Dec 31 17:33:01 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 5 hours | Sun Dec 31 22:32:01 2000 PST - Mon Jan 01 17:32:01 2001 PST | @ 14 secs ago | Mon Jan 01 17:31:47 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 1 min | Mon Jan 01 17:33:01 2001 PST - Sun Dec 31 17:32:01 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Mon Jan 01 19:35:05 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 5 hours | Mon Jan 01 22:32:01 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Jan 02 19:35:05 2001 PST - Sun Dec 31 17:32:01 2000 PST | @ 10 days | Wed Jan 10 17:32:01 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 10 days | Thu Jan 11 17:32:01 2001 PST - Sun Dec 31 17:32:01 2000 PST | @ 3 mons | Sat Mar 31 17:32:01 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 3 mons | Sun Apr 01 17:32:01 2001 PDT - Sun Dec 31 17:32:01 2000 PST | @ 5 mons | Thu May 31 17:32:01 2001 PDT - Sun Dec 31 17:32:01 2000 PST | @ 5 mons 12 hours | Fri Jun 01 05:32:01 2001 PDT - Mon Jan 01 17:32:01 2001 PST | @ 5 mons | Fri Jun 01 17:32:01 2001 PDT - Mon Jan 01 17:32:01 2001 PST | @ 5 mons 12 hours | Sat Jun 02 05:32:01 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 14 secs ago | Sat Sep 22 18:19:06 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 1 min | Sat Sep 22 18:20:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 5 hours | Sat Sep 22 23:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 1 day 2 hours 3 mins 4 secs | Sun Sep 23 20:22:24 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 10 days | Tue Oct 02 18:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 3 mons | Sat Dec 22 18:19:20 2001 PST - Sat Sep 22 18:19:20 2001 PDT | @ 5 mons | Fri Feb 22 18:19:20 2002 PST - Sat Sep 22 18:19:20 2001 PDT | @ 5 mons 12 hours | Sat Feb 23 06:19:20 2002 PST - Wed Feb 28 17:32:01 1996 PST | @ 6 years | Thu Feb 28 17:32:01 2002 PST - Thu Feb 29 17:32:01 1996 PST | @ 6 years | Thu Feb 28 17:32:01 2002 PST - Fri Mar 01 17:32:01 1996 PST | @ 6 years | Fri Mar 01 17:32:01 2002 PST - Mon Dec 30 17:32:01 1996 PST | @ 6 years | Mon Dec 30 17:32:01 2002 PST - Tue Dec 31 17:32:01 1996 PST | @ 6 years | Tue Dec 31 17:32:01 2002 PST - Thu Jan 01 00:00:00 1970 PST | @ 34 years | Thu Jan 01 00:00:00 2004 PST - Fri Dec 31 17:32:01 1999 PST | @ 6 years | Sat Dec 31 17:32:01 2005 PST - Sat Jan 01 17:32:01 2000 PST | @ 6 years | Sun Jan 01 17:32:01 2006 PST - Wed Mar 15 02:14:05 2000 PST | @ 6 years | Wed Mar 15 02:14:05 2006 PST - Wed Mar 15 03:14:04 2000 PST | @ 6 years | Wed Mar 15 03:14:04 2006 PST - Wed Mar 15 08:14:01 2000 PST | @ 6 years | Wed Mar 15 08:14:01 2006 PST - Wed Mar 15 12:14:03 2000 PST | @ 6 years | Wed Mar 15 12:14:03 2006 PST - Wed Mar 15 13:14:02 2000 PST | @ 6 years | Wed Mar 15 13:14:02 2006 PST - Sun Dec 31 17:32:01 2000 PST | @ 6 years | Sun Dec 31 17:32:01 2006 PST - Mon Jan 01 17:32:01 2001 PST | @ 6 years | Mon Jan 01 17:32:01 2007 PST - Sat Sep 22 18:19:20 2001 PDT | @ 6 years | Sat Sep 22 18:19:20 2007 PDT - Wed Feb 28 17:32:01 1996 PST | @ 34 years | Thu Feb 28 17:32:01 2030 PST - Thu Feb 29 17:32:01 1996 PST | @ 34 years | Thu Feb 28 17:32:01 2030 PST - Fri Mar 01 17:32:01 1996 PST | @ 34 years | Fri Mar 01 17:32:01 2030 PST - Mon Dec 30 17:32:01 1996 PST | @ 34 years | Mon Dec 30 17:32:01 2030 PST - Tue Dec 31 17:32:01 1996 PST | @ 34 years | Tue Dec 31 17:32:01 2030 PST - Fri Dec 31 17:32:01 1999 PST | @ 34 years | Sat Dec 31 17:32:01 2033 PST - Sat Jan 01 17:32:01 2000 PST | @ 34 years | Sun Jan 01 17:32:01 2034 PST - Wed Mar 15 02:14:05 2000 PST | @ 34 years | Wed Mar 15 02:14:05 2034 PDT - Wed Mar 15 03:14:04 2000 PST | @ 34 years | Wed Mar 15 03:14:04 2034 PDT - Wed Mar 15 08:14:01 2000 PST | @ 34 years | Wed Mar 15 08:14:01 2034 PDT - Wed Mar 15 12:14:03 2000 PST | @ 34 years | Wed Mar 15 12:14:03 2034 PDT - Wed Mar 15 13:14:02 2000 PST | @ 34 years | Wed Mar 15 13:14:02 2034 PDT - Sun Dec 31 17:32:01 2000 PST | @ 34 years | Sun Dec 31 17:32:01 2034 PST - Mon Jan 01 17:32:01 2001 PST | @ 34 years | Mon Jan 01 17:32:01 2035 PST - Sat Sep 22 18:19:20 2001 PDT | @ 34 years | Sat Sep 22 18:19:20 2035 PDT - Thu Jan 01 00:00:00 1970 PST | infinity | infinity - Wed Feb 28 17:32:01 1996 PST | infinity | infinity - Thu Feb 29 17:32:01 1996 PST | infinity | infinity - Fri Mar 01 17:32:01 1996 PST | infinity | infinity - Mon Dec 30 17:32:01 1996 PST | infinity | infinity - Tue Dec 31 17:32:01 1996 PST | infinity | infinity - Fri Dec 31 17:32:01 1999 PST | infinity | infinity - Sat Jan 01 17:32:01 2000 PST | infinity | infinity - Wed Mar 15 02:14:05 2000 PST | infinity | infinity - Wed Mar 15 03:14:04 2000 PST | infinity | infinity - Wed Mar 15 08:14:01 2000 PST | infinity | infinity - Wed Mar 15 12:14:03 2000 PST | infinity | infinity - Wed Mar 15 13:14:02 2000 PST | infinity | infinity - Sun Dec 31 17:32:01 2000 PST | infinity | infinity - Mon Jan 01 17:32:01 2001 PST | infinity | infinity - Sat Sep 22 18:19:20 2001 PDT | infinity | infinity -(192 rows) - -SELECT d.f1 AS "timestamp", t.f1 AS "interval", d.f1 - t.f1 AS minus - FROM TEMP_TIMESTAMP d, INTERVAL_TBL t - ORDER BY minus, "timestamp", "interval"; - timestamp | interval | minus -------------------------------+-------------------------------+------------------------------ - Thu Jan 01 00:00:00 1970 PST | infinity | -infinity - Wed Feb 28 17:32:01 1996 PST | infinity | -infinity - Thu Feb 29 17:32:01 1996 PST | infinity | -infinity - Fri Mar 01 17:32:01 1996 PST | infinity | -infinity - Mon Dec 30 17:32:01 1996 PST | infinity | -infinity - Tue Dec 31 17:32:01 1996 PST | infinity | -infinity - Fri Dec 31 17:32:01 1999 PST | infinity | -infinity - Sat Jan 01 17:32:01 2000 PST | infinity | -infinity - Wed Mar 15 02:14:05 2000 PST | infinity | -infinity - Wed Mar 15 03:14:04 2000 PST | infinity | -infinity - Wed Mar 15 08:14:01 2000 PST | infinity | -infinity - Wed Mar 15 12:14:03 2000 PST | infinity | -infinity - Wed Mar 15 13:14:02 2000 PST | infinity | -infinity - Sun Dec 31 17:32:01 2000 PST | infinity | -infinity - Mon Jan 01 17:32:01 2001 PST | infinity | -infinity - Sat Sep 22 18:19:20 2001 PDT | infinity | -infinity - Thu Jan 01 00:00:00 1970 PST | @ 34 years | Wed Jan 01 00:00:00 1936 PST - Wed Feb 28 17:32:01 1996 PST | @ 34 years | Wed Feb 28 17:32:01 1962 PST - Thu Feb 29 17:32:01 1996 PST | @ 34 years | Wed Feb 28 17:32:01 1962 PST - Fri Mar 01 17:32:01 1996 PST | @ 34 years | Thu Mar 01 17:32:01 1962 PST - Mon Dec 30 17:32:01 1996 PST | @ 34 years | Sun Dec 30 17:32:01 1962 PST - Tue Dec 31 17:32:01 1996 PST | @ 34 years | Mon Dec 31 17:32:01 1962 PST - Thu Jan 01 00:00:00 1970 PST | @ 6 years | Wed Jan 01 00:00:00 1964 PST - Fri Dec 31 17:32:01 1999 PST | @ 34 years | Fri Dec 31 17:32:01 1965 PST - Sat Jan 01 17:32:01 2000 PST | @ 34 years | Sat Jan 01 17:32:01 1966 PST - Wed Mar 15 02:14:05 2000 PST | @ 34 years | Tue Mar 15 02:14:05 1966 PST - Wed Mar 15 03:14:04 2000 PST | @ 34 years | Tue Mar 15 03:14:04 1966 PST - Wed Mar 15 08:14:01 2000 PST | @ 34 years | Tue Mar 15 08:14:01 1966 PST - Wed Mar 15 12:14:03 2000 PST | @ 34 years | Tue Mar 15 12:14:03 1966 PST - Wed Mar 15 13:14:02 2000 PST | @ 34 years | Tue Mar 15 13:14:02 1966 PST - Sun Dec 31 17:32:01 2000 PST | @ 34 years | Sat Dec 31 17:32:01 1966 PST - Mon Jan 01 17:32:01 2001 PST | @ 34 years | Sun Jan 01 17:32:01 1967 PST - Sat Sep 22 18:19:20 2001 PDT | @ 34 years | Fri Sep 22 18:19:20 1967 PDT - Thu Jan 01 00:00:00 1970 PST | @ 5 mons 12 hours | Thu Jul 31 12:00:00 1969 PDT - Thu Jan 01 00:00:00 1970 PST | @ 5 mons | Fri Aug 01 00:00:00 1969 PDT - Thu Jan 01 00:00:00 1970 PST | @ 3 mons | Wed Oct 01 00:00:00 1969 PDT - Thu Jan 01 00:00:00 1970 PST | @ 10 days | Mon Dec 22 00:00:00 1969 PST - Thu Jan 01 00:00:00 1970 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Dec 30 21:56:56 1969 PST - Thu Jan 01 00:00:00 1970 PST | @ 5 hours | Wed Dec 31 19:00:00 1969 PST - Thu Jan 01 00:00:00 1970 PST | @ 1 min | Wed Dec 31 23:59:00 1969 PST - Thu Jan 01 00:00:00 1970 PST | @ 14 secs ago | Thu Jan 01 00:00:14 1970 PST - Wed Feb 28 17:32:01 1996 PST | @ 6 years | Wed Feb 28 17:32:01 1990 PST - Thu Feb 29 17:32:01 1996 PST | @ 6 years | Wed Feb 28 17:32:01 1990 PST - Fri Mar 01 17:32:01 1996 PST | @ 6 years | Thu Mar 01 17:32:01 1990 PST - Mon Dec 30 17:32:01 1996 PST | @ 6 years | Sun Dec 30 17:32:01 1990 PST - Tue Dec 31 17:32:01 1996 PST | @ 6 years | Mon Dec 31 17:32:01 1990 PST - Fri Dec 31 17:32:01 1999 PST | @ 6 years | Fri Dec 31 17:32:01 1993 PST - Sat Jan 01 17:32:01 2000 PST | @ 6 years | Sat Jan 01 17:32:01 1994 PST - Wed Mar 15 02:14:05 2000 PST | @ 6 years | Tue Mar 15 02:14:05 1994 PST - Wed Mar 15 03:14:04 2000 PST | @ 6 years | Tue Mar 15 03:14:04 1994 PST - Wed Mar 15 08:14:01 2000 PST | @ 6 years | Tue Mar 15 08:14:01 1994 PST - Wed Mar 15 12:14:03 2000 PST | @ 6 years | Tue Mar 15 12:14:03 1994 PST - Wed Mar 15 13:14:02 2000 PST | @ 6 years | Tue Mar 15 13:14:02 1994 PST - Sun Dec 31 17:32:01 2000 PST | @ 6 years | Sat Dec 31 17:32:01 1994 PST - Mon Jan 01 17:32:01 2001 PST | @ 6 years | Sun Jan 01 17:32:01 1995 PST - Sat Sep 22 18:19:20 2001 PDT | @ 6 years | Fri Sep 22 18:19:20 1995 PDT - Wed Feb 28 17:32:01 1996 PST | @ 5 mons 12 hours | Thu Sep 28 05:32:01 1995 PDT - Wed Feb 28 17:32:01 1996 PST | @ 5 mons | Thu Sep 28 17:32:01 1995 PDT - Thu Feb 29 17:32:01 1996 PST | @ 5 mons 12 hours | Fri Sep 29 05:32:01 1995 PDT - Thu Feb 29 17:32:01 1996 PST | @ 5 mons | Fri Sep 29 17:32:01 1995 PDT - Fri Mar 01 17:32:01 1996 PST | @ 5 mons 12 hours | Sun Oct 01 05:32:01 1995 PDT - Fri Mar 01 17:32:01 1996 PST | @ 5 mons | Sun Oct 01 17:32:01 1995 PDT - Wed Feb 28 17:32:01 1996 PST | @ 3 mons | Tue Nov 28 17:32:01 1995 PST - Thu Feb 29 17:32:01 1996 PST | @ 3 mons | Wed Nov 29 17:32:01 1995 PST - Fri Mar 01 17:32:01 1996 PST | @ 3 mons | Fri Dec 01 17:32:01 1995 PST - Wed Feb 28 17:32:01 1996 PST | @ 10 days | Sun Feb 18 17:32:01 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 10 days | Mon Feb 19 17:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 10 days | Tue Feb 20 17:32:01 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Feb 27 15:28:57 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 5 hours | Wed Feb 28 12:32:01 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Wed Feb 28 15:28:57 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 1 min | Wed Feb 28 17:31:01 1996 PST - Wed Feb 28 17:32:01 1996 PST | @ 14 secs ago | Wed Feb 28 17:32:15 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 5 hours | Thu Feb 29 12:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Feb 29 15:28:57 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 1 min | Thu Feb 29 17:31:01 1996 PST - Thu Feb 29 17:32:01 1996 PST | @ 14 secs ago | Thu Feb 29 17:32:15 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 5 hours | Fri Mar 01 12:32:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 1 min | Fri Mar 01 17:31:01 1996 PST - Fri Mar 01 17:32:01 1996 PST | @ 14 secs ago | Fri Mar 01 17:32:15 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 5 mons 12 hours | Tue Jul 30 05:32:01 1996 PDT - Mon Dec 30 17:32:01 1996 PST | @ 5 mons | Tue Jul 30 17:32:01 1996 PDT - Tue Dec 31 17:32:01 1996 PST | @ 5 mons 12 hours | Wed Jul 31 05:32:01 1996 PDT - Tue Dec 31 17:32:01 1996 PST | @ 5 mons | Wed Jul 31 17:32:01 1996 PDT - Mon Dec 30 17:32:01 1996 PST | @ 3 mons | Mon Sep 30 17:32:01 1996 PDT - Tue Dec 31 17:32:01 1996 PST | @ 3 mons | Mon Sep 30 17:32:01 1996 PDT - Mon Dec 30 17:32:01 1996 PST | @ 10 days | Fri Dec 20 17:32:01 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 10 days | Sat Dec 21 17:32:01 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Sun Dec 29 15:28:57 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 5 hours | Mon Dec 30 12:32:01 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 1 day 2 hours 3 mins 4 secs | Mon Dec 30 15:28:57 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 1 min | Mon Dec 30 17:31:01 1996 PST - Mon Dec 30 17:32:01 1996 PST | @ 14 secs ago | Mon Dec 30 17:32:15 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 5 hours | Tue Dec 31 12:32:01 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 1 min | Tue Dec 31 17:31:01 1996 PST - Tue Dec 31 17:32:01 1996 PST | @ 14 secs ago | Tue Dec 31 17:32:15 1996 PST - Fri Dec 31 17:32:01 1999 PST | @ 5 mons 12 hours | Sat Jul 31 05:32:01 1999 PDT - Fri Dec 31 17:32:01 1999 PST | @ 5 mons | Sat Jul 31 17:32:01 1999 PDT - Sat Jan 01 17:32:01 2000 PST | @ 5 mons 12 hours | Sun Aug 01 05:32:01 1999 PDT - Sat Jan 01 17:32:01 2000 PST | @ 5 mons | Sun Aug 01 17:32:01 1999 PDT - Fri Dec 31 17:32:01 1999 PST | @ 3 mons | Thu Sep 30 17:32:01 1999 PDT - Sat Jan 01 17:32:01 2000 PST | @ 3 mons | Fri Oct 01 17:32:01 1999 PDT - Wed Mar 15 02:14:05 2000 PST | @ 5 mons 12 hours | Thu Oct 14 14:14:05 1999 PDT - Wed Mar 15 03:14:04 2000 PST | @ 5 mons 12 hours | Thu Oct 14 15:14:04 1999 PDT - Wed Mar 15 08:14:01 2000 PST | @ 5 mons 12 hours | Thu Oct 14 20:14:01 1999 PDT - Wed Mar 15 12:14:03 2000 PST | @ 5 mons 12 hours | Fri Oct 15 00:14:03 1999 PDT - Wed Mar 15 13:14:02 2000 PST | @ 5 mons 12 hours | Fri Oct 15 01:14:02 1999 PDT - Wed Mar 15 02:14:05 2000 PST | @ 5 mons | Fri Oct 15 02:14:05 1999 PDT - Wed Mar 15 03:14:04 2000 PST | @ 5 mons | Fri Oct 15 03:14:04 1999 PDT - Wed Mar 15 08:14:01 2000 PST | @ 5 mons | Fri Oct 15 08:14:01 1999 PDT - Wed Mar 15 12:14:03 2000 PST | @ 5 mons | Fri Oct 15 12:14:03 1999 PDT - Wed Mar 15 13:14:02 2000 PST | @ 5 mons | Fri Oct 15 13:14:02 1999 PDT - Wed Mar 15 02:14:05 2000 PST | @ 3 mons | Wed Dec 15 02:14:05 1999 PST - Wed Mar 15 03:14:04 2000 PST | @ 3 mons | Wed Dec 15 03:14:04 1999 PST - Wed Mar 15 08:14:01 2000 PST | @ 3 mons | Wed Dec 15 08:14:01 1999 PST - Wed Mar 15 12:14:03 2000 PST | @ 3 mons | Wed Dec 15 12:14:03 1999 PST - Wed Mar 15 13:14:02 2000 PST | @ 3 mons | Wed Dec 15 13:14:02 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 10 days | Tue Dec 21 17:32:01 1999 PST - Sat Jan 01 17:32:01 2000 PST | @ 10 days | Wed Dec 22 17:32:01 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 1 day 2 hours 3 mins 4 secs | Thu Dec 30 15:28:57 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 5 hours | Fri Dec 31 12:32:01 1999 PST - Sat Jan 01 17:32:01 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Fri Dec 31 15:28:57 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 1 min | Fri Dec 31 17:31:01 1999 PST - Fri Dec 31 17:32:01 1999 PST | @ 14 secs ago | Fri Dec 31 17:32:15 1999 PST - Sat Jan 01 17:32:01 2000 PST | @ 5 hours | Sat Jan 01 12:32:01 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 1 min | Sat Jan 01 17:31:01 2000 PST - Sat Jan 01 17:32:01 2000 PST | @ 14 secs ago | Sat Jan 01 17:32:15 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 10 days | Sun Mar 05 02:14:05 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 10 days | Sun Mar 05 03:14:04 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 10 days | Sun Mar 05 08:14:01 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 10 days | Sun Mar 05 12:14:03 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 10 days | Sun Mar 05 13:14:02 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Mar 14 00:11:01 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Mar 14 01:11:00 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Mar 14 06:10:57 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Mar 14 10:10:59 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Tue Mar 14 11:10:58 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 5 hours | Tue Mar 14 21:14:05 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 5 hours | Tue Mar 14 22:14:04 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 1 min | Wed Mar 15 02:13:05 2000 PST - Wed Mar 15 02:14:05 2000 PST | @ 14 secs ago | Wed Mar 15 02:14:19 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 1 min | Wed Mar 15 03:13:04 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 5 hours | Wed Mar 15 03:14:01 2000 PST - Wed Mar 15 03:14:04 2000 PST | @ 14 secs ago | Wed Mar 15 03:14:18 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 5 hours | Wed Mar 15 07:14:03 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 1 min | Wed Mar 15 08:13:01 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 5 hours | Wed Mar 15 08:14:02 2000 PST - Wed Mar 15 08:14:01 2000 PST | @ 14 secs ago | Wed Mar 15 08:14:15 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 1 min | Wed Mar 15 12:13:03 2000 PST - Wed Mar 15 12:14:03 2000 PST | @ 14 secs ago | Wed Mar 15 12:14:17 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 1 min | Wed Mar 15 13:13:02 2000 PST - Wed Mar 15 13:14:02 2000 PST | @ 14 secs ago | Wed Mar 15 13:14:16 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 5 mons 12 hours | Mon Jul 31 05:32:01 2000 PDT - Sun Dec 31 17:32:01 2000 PST | @ 5 mons | Mon Jul 31 17:32:01 2000 PDT - Mon Jan 01 17:32:01 2001 PST | @ 5 mons 12 hours | Tue Aug 01 05:32:01 2000 PDT - Mon Jan 01 17:32:01 2001 PST | @ 5 mons | Tue Aug 01 17:32:01 2000 PDT - Sun Dec 31 17:32:01 2000 PST | @ 3 mons | Sat Sep 30 17:32:01 2000 PDT - Mon Jan 01 17:32:01 2001 PST | @ 3 mons | Sun Oct 01 17:32:01 2000 PDT - Sun Dec 31 17:32:01 2000 PST | @ 10 days | Thu Dec 21 17:32:01 2000 PST - Mon Jan 01 17:32:01 2001 PST | @ 10 days | Fri Dec 22 17:32:01 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 1 day 2 hours 3 mins 4 secs | Sat Dec 30 15:28:57 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 5 hours | Sun Dec 31 12:32:01 2000 PST - Mon Jan 01 17:32:01 2001 PST | @ 1 day 2 hours 3 mins 4 secs | Sun Dec 31 15:28:57 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 1 min | Sun Dec 31 17:31:01 2000 PST - Sun Dec 31 17:32:01 2000 PST | @ 14 secs ago | Sun Dec 31 17:32:15 2000 PST - Mon Jan 01 17:32:01 2001 PST | @ 5 hours | Mon Jan 01 12:32:01 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 1 min | Mon Jan 01 17:31:01 2001 PST - Mon Jan 01 17:32:01 2001 PST | @ 14 secs ago | Mon Jan 01 17:32:15 2001 PST - Sat Sep 22 18:19:20 2001 PDT | @ 5 mons 12 hours | Sun Apr 22 06:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 5 mons | Sun Apr 22 18:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 3 mons | Fri Jun 22 18:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 10 days | Wed Sep 12 18:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 1 day 2 hours 3 mins 4 secs | Fri Sep 21 16:16:16 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 5 hours | Sat Sep 22 13:19:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 1 min | Sat Sep 22 18:18:20 2001 PDT - Sat Sep 22 18:19:20 2001 PDT | @ 14 secs ago | Sat Sep 22 18:19:34 2001 PDT - Thu Jan 01 00:00:00 1970 PST | -infinity | infinity - Wed Feb 28 17:32:01 1996 PST | -infinity | infinity - Thu Feb 29 17:32:01 1996 PST | -infinity | infinity - Fri Mar 01 17:32:01 1996 PST | -infinity | infinity - Mon Dec 30 17:32:01 1996 PST | -infinity | infinity - Tue Dec 31 17:32:01 1996 PST | -infinity | infinity - Fri Dec 31 17:32:01 1999 PST | -infinity | infinity - Sat Jan 01 17:32:01 2000 PST | -infinity | infinity - Wed Mar 15 02:14:05 2000 PST | -infinity | infinity - Wed Mar 15 03:14:04 2000 PST | -infinity | infinity - Wed Mar 15 08:14:01 2000 PST | -infinity | infinity - Wed Mar 15 12:14:03 2000 PST | -infinity | infinity - Wed Mar 15 13:14:02 2000 PST | -infinity | infinity - Sun Dec 31 17:32:01 2000 PST | -infinity | infinity - Mon Jan 01 17:32:01 2001 PST | -infinity | infinity - Sat Sep 22 18:19:20 2001 PDT | -infinity | infinity -(192 rows) - -SELECT d.f1 AS "timestamp", - timestamp with time zone '1980-01-06 00:00 GMT' AS gpstime_zero, - d.f1 - timestamp with time zone '1980-01-06 00:00 GMT' AS difference - FROM TEMP_TIMESTAMP d - ORDER BY difference; - timestamp | gpstime_zero | difference -------------------------------+------------------------------+------------------------------------- - Thu Jan 01 00:00:00 1970 PST | Sat Jan 05 16:00:00 1980 PST | @ 3656 days 16 hours ago - Wed Feb 28 17:32:01 1996 PST | Sat Jan 05 16:00:00 1980 PST | @ 5898 days 1 hour 32 mins 1 sec - Thu Feb 29 17:32:01 1996 PST | Sat Jan 05 16:00:00 1980 PST | @ 5899 days 1 hour 32 mins 1 sec - Fri Mar 01 17:32:01 1996 PST | Sat Jan 05 16:00:00 1980 PST | @ 5900 days 1 hour 32 mins 1 sec - Mon Dec 30 17:32:01 1996 PST | Sat Jan 05 16:00:00 1980 PST | @ 6204 days 1 hour 32 mins 1 sec - Tue Dec 31 17:32:01 1996 PST | Sat Jan 05 16:00:00 1980 PST | @ 6205 days 1 hour 32 mins 1 sec - Fri Dec 31 17:32:01 1999 PST | Sat Jan 05 16:00:00 1980 PST | @ 7300 days 1 hour 32 mins 1 sec - Sat Jan 01 17:32:01 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7301 days 1 hour 32 mins 1 sec - Wed Mar 15 02:14:05 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7374 days 10 hours 14 mins 5 secs - Wed Mar 15 03:14:04 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7374 days 11 hours 14 mins 4 secs - Wed Mar 15 08:14:01 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7374 days 16 hours 14 mins 1 sec - Wed Mar 15 12:14:03 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7374 days 20 hours 14 mins 3 secs - Wed Mar 15 13:14:02 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7374 days 21 hours 14 mins 2 secs - Sun Dec 31 17:32:01 2000 PST | Sat Jan 05 16:00:00 1980 PST | @ 7666 days 1 hour 32 mins 1 sec - Mon Jan 01 17:32:01 2001 PST | Sat Jan 05 16:00:00 1980 PST | @ 7667 days 1 hour 32 mins 1 sec - Sat Sep 22 18:19:20 2001 PDT | Sat Jan 05 16:00:00 1980 PST | @ 7931 days 1 hour 19 mins 20 secs -(16 rows) - -SELECT d1.f1 AS timestamp1, d2.f1 AS timestamp2, d1.f1 - d2.f1 AS difference - FROM TEMP_TIMESTAMP d1, TEMP_TIMESTAMP d2 - ORDER BY timestamp1, timestamp2, difference; - timestamp1 | timestamp2 | difference -------------------------------+------------------------------+------------------------------------------- - Thu Jan 01 00:00:00 1970 PST | Thu Jan 01 00:00:00 1970 PST | @ 0 - Thu Jan 01 00:00:00 1970 PST | Wed Feb 28 17:32:01 1996 PST | @ 9554 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Thu Feb 29 17:32:01 1996 PST | @ 9555 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Fri Mar 01 17:32:01 1996 PST | @ 9556 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Mon Dec 30 17:32:01 1996 PST | @ 9860 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Tue Dec 31 17:32:01 1996 PST | @ 9861 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Fri Dec 31 17:32:01 1999 PST | @ 10956 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Sat Jan 01 17:32:01 2000 PST | @ 10957 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Wed Mar 15 02:14:05 2000 PST | @ 11031 days 2 hours 14 mins 5 secs ago - Thu Jan 01 00:00:00 1970 PST | Wed Mar 15 03:14:04 2000 PST | @ 11031 days 3 hours 14 mins 4 secs ago - Thu Jan 01 00:00:00 1970 PST | Wed Mar 15 08:14:01 2000 PST | @ 11031 days 8 hours 14 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Wed Mar 15 12:14:03 2000 PST | @ 11031 days 12 hours 14 mins 3 secs ago - Thu Jan 01 00:00:00 1970 PST | Wed Mar 15 13:14:02 2000 PST | @ 11031 days 13 hours 14 mins 2 secs ago - Thu Jan 01 00:00:00 1970 PST | Sun Dec 31 17:32:01 2000 PST | @ 11322 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Mon Jan 01 17:32:01 2001 PST | @ 11323 days 17 hours 32 mins 1 sec ago - Thu Jan 01 00:00:00 1970 PST | Sat Sep 22 18:19:20 2001 PDT | @ 11587 days 17 hours 19 mins 20 secs ago - Wed Feb 28 17:32:01 1996 PST | Thu Jan 01 00:00:00 1970 PST | @ 9554 days 17 hours 32 mins 1 sec - Wed Feb 28 17:32:01 1996 PST | Wed Feb 28 17:32:01 1996 PST | @ 0 - Wed Feb 28 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | @ 1 day ago - Wed Feb 28 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST | @ 2 days ago - Wed Feb 28 17:32:01 1996 PST | Mon Dec 30 17:32:01 1996 PST | @ 306 days ago - Wed Feb 28 17:32:01 1996 PST | Tue Dec 31 17:32:01 1996 PST | @ 307 days ago - Wed Feb 28 17:32:01 1996 PST | Fri Dec 31 17:32:01 1999 PST | @ 1402 days ago - Wed Feb 28 17:32:01 1996 PST | Sat Jan 01 17:32:01 2000 PST | @ 1403 days ago - Wed Feb 28 17:32:01 1996 PST | Wed Mar 15 02:14:05 2000 PST | @ 1476 days 8 hours 42 mins 4 secs ago - Wed Feb 28 17:32:01 1996 PST | Wed Mar 15 03:14:04 2000 PST | @ 1476 days 9 hours 42 mins 3 secs ago - Wed Feb 28 17:32:01 1996 PST | Wed Mar 15 08:14:01 2000 PST | @ 1476 days 14 hours 42 mins ago - Wed Feb 28 17:32:01 1996 PST | Wed Mar 15 12:14:03 2000 PST | @ 1476 days 18 hours 42 mins 2 secs ago - Wed Feb 28 17:32:01 1996 PST | Wed Mar 15 13:14:02 2000 PST | @ 1476 days 19 hours 42 mins 1 sec ago - Wed Feb 28 17:32:01 1996 PST | Sun Dec 31 17:32:01 2000 PST | @ 1768 days ago - Wed Feb 28 17:32:01 1996 PST | Mon Jan 01 17:32:01 2001 PST | @ 1769 days ago - Wed Feb 28 17:32:01 1996 PST | Sat Sep 22 18:19:20 2001 PDT | @ 2032 days 23 hours 47 mins 19 secs ago - Thu Feb 29 17:32:01 1996 PST | Thu Jan 01 00:00:00 1970 PST | @ 9555 days 17 hours 32 mins 1 sec - Thu Feb 29 17:32:01 1996 PST | Wed Feb 28 17:32:01 1996 PST | @ 1 day - Thu Feb 29 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | @ 0 - Thu Feb 29 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST | @ 1 day ago - Thu Feb 29 17:32:01 1996 PST | Mon Dec 30 17:32:01 1996 PST | @ 305 days ago - Thu Feb 29 17:32:01 1996 PST | Tue Dec 31 17:32:01 1996 PST | @ 306 days ago - Thu Feb 29 17:32:01 1996 PST | Fri Dec 31 17:32:01 1999 PST | @ 1401 days ago - Thu Feb 29 17:32:01 1996 PST | Sat Jan 01 17:32:01 2000 PST | @ 1402 days ago - Thu Feb 29 17:32:01 1996 PST | Wed Mar 15 02:14:05 2000 PST | @ 1475 days 8 hours 42 mins 4 secs ago - Thu Feb 29 17:32:01 1996 PST | Wed Mar 15 03:14:04 2000 PST | @ 1475 days 9 hours 42 mins 3 secs ago - Thu Feb 29 17:32:01 1996 PST | Wed Mar 15 08:14:01 2000 PST | @ 1475 days 14 hours 42 mins ago - Thu Feb 29 17:32:01 1996 PST | Wed Mar 15 12:14:03 2000 PST | @ 1475 days 18 hours 42 mins 2 secs ago - Thu Feb 29 17:32:01 1996 PST | Wed Mar 15 13:14:02 2000 PST | @ 1475 days 19 hours 42 mins 1 sec ago - Thu Feb 29 17:32:01 1996 PST | Sun Dec 31 17:32:01 2000 PST | @ 1767 days ago - Thu Feb 29 17:32:01 1996 PST | Mon Jan 01 17:32:01 2001 PST | @ 1768 days ago - Thu Feb 29 17:32:01 1996 PST | Sat Sep 22 18:19:20 2001 PDT | @ 2031 days 23 hours 47 mins 19 secs ago - Fri Mar 01 17:32:01 1996 PST | Thu Jan 01 00:00:00 1970 PST | @ 9556 days 17 hours 32 mins 1 sec - Fri Mar 01 17:32:01 1996 PST | Wed Feb 28 17:32:01 1996 PST | @ 2 days - Fri Mar 01 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | @ 1 day - Fri Mar 01 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST | @ 0 - Fri Mar 01 17:32:01 1996 PST | Mon Dec 30 17:32:01 1996 PST | @ 304 days ago - Fri Mar 01 17:32:01 1996 PST | Tue Dec 31 17:32:01 1996 PST | @ 305 days ago - Fri Mar 01 17:32:01 1996 PST | Fri Dec 31 17:32:01 1999 PST | @ 1400 days ago - Fri Mar 01 17:32:01 1996 PST | Sat Jan 01 17:32:01 2000 PST | @ 1401 days ago - Fri Mar 01 17:32:01 1996 PST | Wed Mar 15 02:14:05 2000 PST | @ 1474 days 8 hours 42 mins 4 secs ago - Fri Mar 01 17:32:01 1996 PST | Wed Mar 15 03:14:04 2000 PST | @ 1474 days 9 hours 42 mins 3 secs ago - Fri Mar 01 17:32:01 1996 PST | Wed Mar 15 08:14:01 2000 PST | @ 1474 days 14 hours 42 mins ago - Fri Mar 01 17:32:01 1996 PST | Wed Mar 15 12:14:03 2000 PST | @ 1474 days 18 hours 42 mins 2 secs ago - Fri Mar 01 17:32:01 1996 PST | Wed Mar 15 13:14:02 2000 PST | @ 1474 days 19 hours 42 mins 1 sec ago - Fri Mar 01 17:32:01 1996 PST | Sun Dec 31 17:32:01 2000 PST | @ 1766 days ago - Fri Mar 01 17:32:01 1996 PST | Mon Jan 01 17:32:01 2001 PST | @ 1767 days ago - Fri Mar 01 17:32:01 1996 PST | Sat Sep 22 18:19:20 2001 PDT | @ 2030 days 23 hours 47 mins 19 secs ago - Mon Dec 30 17:32:01 1996 PST | Thu Jan 01 00:00:00 1970 PST | @ 9860 days 17 hours 32 mins 1 sec - Mon Dec 30 17:32:01 1996 PST | Wed Feb 28 17:32:01 1996 PST | @ 306 days - Mon Dec 30 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | @ 305 days - Mon Dec 30 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST | @ 304 days - Mon Dec 30 17:32:01 1996 PST | Mon Dec 30 17:32:01 1996 PST | @ 0 - Mon Dec 30 17:32:01 1996 PST | Tue Dec 31 17:32:01 1996 PST | @ 1 day ago - Mon Dec 30 17:32:01 1996 PST | Fri Dec 31 17:32:01 1999 PST | @ 1096 days ago - Mon Dec 30 17:32:01 1996 PST | Sat Jan 01 17:32:01 2000 PST | @ 1097 days ago - Mon Dec 30 17:32:01 1996 PST | Wed Mar 15 02:14:05 2000 PST | @ 1170 days 8 hours 42 mins 4 secs ago - Mon Dec 30 17:32:01 1996 PST | Wed Mar 15 03:14:04 2000 PST | @ 1170 days 9 hours 42 mins 3 secs ago - Mon Dec 30 17:32:01 1996 PST | Wed Mar 15 08:14:01 2000 PST | @ 1170 days 14 hours 42 mins ago - Mon Dec 30 17:32:01 1996 PST | Wed Mar 15 12:14:03 2000 PST | @ 1170 days 18 hours 42 mins 2 secs ago - Mon Dec 30 17:32:01 1996 PST | Wed Mar 15 13:14:02 2000 PST | @ 1170 days 19 hours 42 mins 1 sec ago - Mon Dec 30 17:32:01 1996 PST | Sun Dec 31 17:32:01 2000 PST | @ 1462 days ago - Mon Dec 30 17:32:01 1996 PST | Mon Jan 01 17:32:01 2001 PST | @ 1463 days ago - Mon Dec 30 17:32:01 1996 PST | Sat Sep 22 18:19:20 2001 PDT | @ 1726 days 23 hours 47 mins 19 secs ago - Tue Dec 31 17:32:01 1996 PST | Thu Jan 01 00:00:00 1970 PST | @ 9861 days 17 hours 32 mins 1 sec - Tue Dec 31 17:32:01 1996 PST | Wed Feb 28 17:32:01 1996 PST | @ 307 days - Tue Dec 31 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | @ 306 days - Tue Dec 31 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST | @ 305 days - Tue Dec 31 17:32:01 1996 PST | Mon Dec 30 17:32:01 1996 PST | @ 1 day - Tue Dec 31 17:32:01 1996 PST | Tue Dec 31 17:32:01 1996 PST | @ 0 - Tue Dec 31 17:32:01 1996 PST | Fri Dec 31 17:32:01 1999 PST | @ 1095 days ago - Tue Dec 31 17:32:01 1996 PST | Sat Jan 01 17:32:01 2000 PST | @ 1096 days ago - Tue Dec 31 17:32:01 1996 PST | Wed Mar 15 02:14:05 2000 PST | @ 1169 days 8 hours 42 mins 4 secs ago - Tue Dec 31 17:32:01 1996 PST | Wed Mar 15 03:14:04 2000 PST | @ 1169 days 9 hours 42 mins 3 secs ago - Tue Dec 31 17:32:01 1996 PST | Wed Mar 15 08:14:01 2000 PST | @ 1169 days 14 hours 42 mins ago - Tue Dec 31 17:32:01 1996 PST | Wed Mar 15 12:14:03 2000 PST | @ 1169 days 18 hours 42 mins 2 secs ago - Tue Dec 31 17:32:01 1996 PST | Wed Mar 15 13:14:02 2000 PST | @ 1169 days 19 hours 42 mins 1 sec ago - Tue Dec 31 17:32:01 1996 PST | Sun Dec 31 17:32:01 2000 PST | @ 1461 days ago - Tue Dec 31 17:32:01 1996 PST | Mon Jan 01 17:32:01 2001 PST | @ 1462 days ago - Tue Dec 31 17:32:01 1996 PST | Sat Sep 22 18:19:20 2001 PDT | @ 1725 days 23 hours 47 mins 19 secs ago - Fri Dec 31 17:32:01 1999 PST | Thu Jan 01 00:00:00 1970 PST | @ 10956 days 17 hours 32 mins 1 sec - Fri Dec 31 17:32:01 1999 PST | Wed Feb 28 17:32:01 1996 PST | @ 1402 days - Fri Dec 31 17:32:01 1999 PST | Thu Feb 29 17:32:01 1996 PST | @ 1401 days - Fri Dec 31 17:32:01 1999 PST | Fri Mar 01 17:32:01 1996 PST | @ 1400 days - Fri Dec 31 17:32:01 1999 PST | Mon Dec 30 17:32:01 1996 PST | @ 1096 days - Fri Dec 31 17:32:01 1999 PST | Tue Dec 31 17:32:01 1996 PST | @ 1095 days - Fri Dec 31 17:32:01 1999 PST | Fri Dec 31 17:32:01 1999 PST | @ 0 - Fri Dec 31 17:32:01 1999 PST | Sat Jan 01 17:32:01 2000 PST | @ 1 day ago - Fri Dec 31 17:32:01 1999 PST | Wed Mar 15 02:14:05 2000 PST | @ 74 days 8 hours 42 mins 4 secs ago - Fri Dec 31 17:32:01 1999 PST | Wed Mar 15 03:14:04 2000 PST | @ 74 days 9 hours 42 mins 3 secs ago - Fri Dec 31 17:32:01 1999 PST | Wed Mar 15 08:14:01 2000 PST | @ 74 days 14 hours 42 mins ago - Fri Dec 31 17:32:01 1999 PST | Wed Mar 15 12:14:03 2000 PST | @ 74 days 18 hours 42 mins 2 secs ago - Fri Dec 31 17:32:01 1999 PST | Wed Mar 15 13:14:02 2000 PST | @ 74 days 19 hours 42 mins 1 sec ago - Fri Dec 31 17:32:01 1999 PST | Sun Dec 31 17:32:01 2000 PST | @ 366 days ago - Fri Dec 31 17:32:01 1999 PST | Mon Jan 01 17:32:01 2001 PST | @ 367 days ago - Fri Dec 31 17:32:01 1999 PST | Sat Sep 22 18:19:20 2001 PDT | @ 630 days 23 hours 47 mins 19 secs ago - Sat Jan 01 17:32:01 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 10957 days 17 hours 32 mins 1 sec - Sat Jan 01 17:32:01 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1403 days - Sat Jan 01 17:32:01 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1402 days - Sat Jan 01 17:32:01 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1401 days - Sat Jan 01 17:32:01 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1097 days - Sat Jan 01 17:32:01 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1096 days - Sat Jan 01 17:32:01 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 1 day - Sat Jan 01 17:32:01 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 0 - Sat Jan 01 17:32:01 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 73 days 8 hours 42 mins 4 secs ago - Sat Jan 01 17:32:01 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 73 days 9 hours 42 mins 3 secs ago - Sat Jan 01 17:32:01 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 73 days 14 hours 42 mins ago - Sat Jan 01 17:32:01 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 73 days 18 hours 42 mins 2 secs ago - Sat Jan 01 17:32:01 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 73 days 19 hours 42 mins 1 sec ago - Sat Jan 01 17:32:01 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 365 days ago - Sat Jan 01 17:32:01 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 366 days ago - Sat Jan 01 17:32:01 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 629 days 23 hours 47 mins 19 secs ago - Wed Mar 15 02:14:05 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 11031 days 2 hours 14 mins 5 secs - Wed Mar 15 02:14:05 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1476 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1475 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1474 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1170 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1169 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 74 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 73 days 8 hours 42 mins 4 secs - Wed Mar 15 02:14:05 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 0 - Wed Mar 15 02:14:05 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 59 mins 59 secs ago - Wed Mar 15 02:14:05 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 5 hours 59 mins 56 secs ago - Wed Mar 15 02:14:05 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 9 hours 59 mins 58 secs ago - Wed Mar 15 02:14:05 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 10 hours 59 mins 57 secs ago - Wed Mar 15 02:14:05 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 291 days 15 hours 17 mins 56 secs ago - Wed Mar 15 02:14:05 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 292 days 15 hours 17 mins 56 secs ago - Wed Mar 15 02:14:05 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 556 days 15 hours 5 mins 15 secs ago - Wed Mar 15 03:14:04 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 11031 days 3 hours 14 mins 4 secs - Wed Mar 15 03:14:04 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1476 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1475 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1474 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1170 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1169 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 74 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 73 days 9 hours 42 mins 3 secs - Wed Mar 15 03:14:04 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 59 mins 59 secs - Wed Mar 15 03:14:04 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 0 - Wed Mar 15 03:14:04 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 4 hours 59 mins 57 secs ago - Wed Mar 15 03:14:04 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 8 hours 59 mins 59 secs ago - Wed Mar 15 03:14:04 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 9 hours 59 mins 58 secs ago - Wed Mar 15 03:14:04 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 291 days 14 hours 17 mins 57 secs ago - Wed Mar 15 03:14:04 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 292 days 14 hours 17 mins 57 secs ago - Wed Mar 15 03:14:04 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 556 days 14 hours 5 mins 16 secs ago - Wed Mar 15 08:14:01 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 11031 days 8 hours 14 mins 1 sec - Wed Mar 15 08:14:01 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1476 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1475 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1474 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1170 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1169 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 74 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 73 days 14 hours 42 mins - Wed Mar 15 08:14:01 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 5 hours 59 mins 56 secs - Wed Mar 15 08:14:01 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 4 hours 59 mins 57 secs - Wed Mar 15 08:14:01 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 0 - Wed Mar 15 08:14:01 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 4 hours 2 secs ago - Wed Mar 15 08:14:01 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 5 hours 1 sec ago - Wed Mar 15 08:14:01 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 291 days 9 hours 18 mins ago - Wed Mar 15 08:14:01 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 292 days 9 hours 18 mins ago - Wed Mar 15 08:14:01 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 556 days 9 hours 5 mins 19 secs ago - Wed Mar 15 12:14:03 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 11031 days 12 hours 14 mins 3 secs - Wed Mar 15 12:14:03 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1476 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1475 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1474 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1170 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1169 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 74 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 73 days 18 hours 42 mins 2 secs - Wed Mar 15 12:14:03 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 9 hours 59 mins 58 secs - Wed Mar 15 12:14:03 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 8 hours 59 mins 59 secs - Wed Mar 15 12:14:03 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 4 hours 2 secs - Wed Mar 15 12:14:03 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 0 - Wed Mar 15 12:14:03 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 59 mins 59 secs ago - Wed Mar 15 12:14:03 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 291 days 5 hours 17 mins 58 secs ago - Wed Mar 15 12:14:03 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 292 days 5 hours 17 mins 58 secs ago - Wed Mar 15 12:14:03 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 556 days 5 hours 5 mins 17 secs ago - Wed Mar 15 13:14:02 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 11031 days 13 hours 14 mins 2 secs - Wed Mar 15 13:14:02 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1476 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1475 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1474 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1170 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1169 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 74 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 73 days 19 hours 42 mins 1 sec - Wed Mar 15 13:14:02 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 10 hours 59 mins 57 secs - Wed Mar 15 13:14:02 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 9 hours 59 mins 58 secs - Wed Mar 15 13:14:02 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 5 hours 1 sec - Wed Mar 15 13:14:02 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 59 mins 59 secs - Wed Mar 15 13:14:02 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 0 - Wed Mar 15 13:14:02 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 291 days 4 hours 17 mins 59 secs ago - Wed Mar 15 13:14:02 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 292 days 4 hours 17 mins 59 secs ago - Wed Mar 15 13:14:02 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 556 days 4 hours 5 mins 18 secs ago - Sun Dec 31 17:32:01 2000 PST | Thu Jan 01 00:00:00 1970 PST | @ 11322 days 17 hours 32 mins 1 sec - Sun Dec 31 17:32:01 2000 PST | Wed Feb 28 17:32:01 1996 PST | @ 1768 days - Sun Dec 31 17:32:01 2000 PST | Thu Feb 29 17:32:01 1996 PST | @ 1767 days - Sun Dec 31 17:32:01 2000 PST | Fri Mar 01 17:32:01 1996 PST | @ 1766 days - Sun Dec 31 17:32:01 2000 PST | Mon Dec 30 17:32:01 1996 PST | @ 1462 days - Sun Dec 31 17:32:01 2000 PST | Tue Dec 31 17:32:01 1996 PST | @ 1461 days - Sun Dec 31 17:32:01 2000 PST | Fri Dec 31 17:32:01 1999 PST | @ 366 days - Sun Dec 31 17:32:01 2000 PST | Sat Jan 01 17:32:01 2000 PST | @ 365 days - Sun Dec 31 17:32:01 2000 PST | Wed Mar 15 02:14:05 2000 PST | @ 291 days 15 hours 17 mins 56 secs - Sun Dec 31 17:32:01 2000 PST | Wed Mar 15 03:14:04 2000 PST | @ 291 days 14 hours 17 mins 57 secs - Sun Dec 31 17:32:01 2000 PST | Wed Mar 15 08:14:01 2000 PST | @ 291 days 9 hours 18 mins - Sun Dec 31 17:32:01 2000 PST | Wed Mar 15 12:14:03 2000 PST | @ 291 days 5 hours 17 mins 58 secs - Sun Dec 31 17:32:01 2000 PST | Wed Mar 15 13:14:02 2000 PST | @ 291 days 4 hours 17 mins 59 secs - Sun Dec 31 17:32:01 2000 PST | Sun Dec 31 17:32:01 2000 PST | @ 0 - Sun Dec 31 17:32:01 2000 PST | Mon Jan 01 17:32:01 2001 PST | @ 1 day ago - Sun Dec 31 17:32:01 2000 PST | Sat Sep 22 18:19:20 2001 PDT | @ 264 days 23 hours 47 mins 19 secs ago - Mon Jan 01 17:32:01 2001 PST | Thu Jan 01 00:00:00 1970 PST | @ 11323 days 17 hours 32 mins 1 sec - Mon Jan 01 17:32:01 2001 PST | Wed Feb 28 17:32:01 1996 PST | @ 1769 days - Mon Jan 01 17:32:01 2001 PST | Thu Feb 29 17:32:01 1996 PST | @ 1768 days - Mon Jan 01 17:32:01 2001 PST | Fri Mar 01 17:32:01 1996 PST | @ 1767 days - Mon Jan 01 17:32:01 2001 PST | Mon Dec 30 17:32:01 1996 PST | @ 1463 days - Mon Jan 01 17:32:01 2001 PST | Tue Dec 31 17:32:01 1996 PST | @ 1462 days - Mon Jan 01 17:32:01 2001 PST | Fri Dec 31 17:32:01 1999 PST | @ 367 days - Mon Jan 01 17:32:01 2001 PST | Sat Jan 01 17:32:01 2000 PST | @ 366 days - Mon Jan 01 17:32:01 2001 PST | Wed Mar 15 02:14:05 2000 PST | @ 292 days 15 hours 17 mins 56 secs - Mon Jan 01 17:32:01 2001 PST | Wed Mar 15 03:14:04 2000 PST | @ 292 days 14 hours 17 mins 57 secs - Mon Jan 01 17:32:01 2001 PST | Wed Mar 15 08:14:01 2000 PST | @ 292 days 9 hours 18 mins - Mon Jan 01 17:32:01 2001 PST | Wed Mar 15 12:14:03 2000 PST | @ 292 days 5 hours 17 mins 58 secs - Mon Jan 01 17:32:01 2001 PST | Wed Mar 15 13:14:02 2000 PST | @ 292 days 4 hours 17 mins 59 secs - Mon Jan 01 17:32:01 2001 PST | Sun Dec 31 17:32:01 2000 PST | @ 1 day - Mon Jan 01 17:32:01 2001 PST | Mon Jan 01 17:32:01 2001 PST | @ 0 - Mon Jan 01 17:32:01 2001 PST | Sat Sep 22 18:19:20 2001 PDT | @ 263 days 23 hours 47 mins 19 secs ago - Sat Sep 22 18:19:20 2001 PDT | Thu Jan 01 00:00:00 1970 PST | @ 11587 days 17 hours 19 mins 20 secs - Sat Sep 22 18:19:20 2001 PDT | Wed Feb 28 17:32:01 1996 PST | @ 2032 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Thu Feb 29 17:32:01 1996 PST | @ 2031 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Fri Mar 01 17:32:01 1996 PST | @ 2030 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Mon Dec 30 17:32:01 1996 PST | @ 1726 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Tue Dec 31 17:32:01 1996 PST | @ 1725 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Fri Dec 31 17:32:01 1999 PST | @ 630 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Sat Jan 01 17:32:01 2000 PST | @ 629 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Wed Mar 15 02:14:05 2000 PST | @ 556 days 15 hours 5 mins 15 secs - Sat Sep 22 18:19:20 2001 PDT | Wed Mar 15 03:14:04 2000 PST | @ 556 days 14 hours 5 mins 16 secs - Sat Sep 22 18:19:20 2001 PDT | Wed Mar 15 08:14:01 2000 PST | @ 556 days 9 hours 5 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Wed Mar 15 12:14:03 2000 PST | @ 556 days 5 hours 5 mins 17 secs - Sat Sep 22 18:19:20 2001 PDT | Wed Mar 15 13:14:02 2000 PST | @ 556 days 4 hours 5 mins 18 secs - Sat Sep 22 18:19:20 2001 PDT | Sun Dec 31 17:32:01 2000 PST | @ 264 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Mon Jan 01 17:32:01 2001 PST | @ 263 days 23 hours 47 mins 19 secs - Sat Sep 22 18:19:20 2001 PDT | Sat Sep 22 18:19:20 2001 PDT | @ 0 -(256 rows) - --- --- Conversions --- -SELECT f1 AS "timestamp", date(f1) AS date - FROM TEMP_TIMESTAMP - WHERE f1 <> timestamp 'now' - ORDER BY date, "timestamp"; - timestamp | date -------------------------------+------------ - Thu Jan 01 00:00:00 1970 PST | 01-01-1970 - Wed Feb 28 17:32:01 1996 PST | 02-28-1996 - Thu Feb 29 17:32:01 1996 PST | 02-29-1996 - Fri Mar 01 17:32:01 1996 PST | 03-01-1996 - Mon Dec 30 17:32:01 1996 PST | 12-30-1996 - Tue Dec 31 17:32:01 1996 PST | 12-31-1996 - Fri Dec 31 17:32:01 1999 PST | 12-31-1999 - Sat Jan 01 17:32:01 2000 PST | 01-01-2000 - Wed Mar 15 02:14:05 2000 PST | 03-15-2000 - Wed Mar 15 03:14:04 2000 PST | 03-15-2000 - Wed Mar 15 08:14:01 2000 PST | 03-15-2000 - Wed Mar 15 12:14:03 2000 PST | 03-15-2000 - Wed Mar 15 13:14:02 2000 PST | 03-15-2000 - Sun Dec 31 17:32:01 2000 PST | 12-31-2000 - Mon Jan 01 17:32:01 2001 PST | 01-01-2001 - Sat Sep 22 18:19:20 2001 PDT | 09-22-2001 -(16 rows) - -DROP TABLE TEMP_TIMESTAMP; --- --- Comparisons between datetime types, especially overflow cases ---- -SELECT '2202020-10-05'::date::timestamp; -- fail -ERROR: date out of range for timestamp -SELECT '2202020-10-05'::date > '2020-10-05'::timestamp as t; - t ---- - t -(1 row) - -SELECT '2020-10-05'::timestamp > '2202020-10-05'::date as f; - f ---- - f -(1 row) - -SELECT '2202020-10-05'::date::timestamptz; -- fail -ERROR: date out of range for timestamp -SELECT '2202020-10-05'::date > '2020-10-05'::timestamptz as t; - t ---- - t -(1 row) - -SELECT '2020-10-05'::timestamptz > '2202020-10-05'::date as f; - f ---- - f -(1 row) - --- This conversion may work depending on timezone -SELECT '4714-11-24 BC'::date::timestamptz; - timestamptz ---------------------------------- - Mon Nov 24 00:00:00 4714 LMT BC -(1 row) - -SET TimeZone = 'UTC-2'; -SELECT '4714-11-24 BC'::date::timestamptz; -- fail -ERROR: date out of range for timestamp -SELECT '4714-11-24 BC'::date < '2020-10-05'::timestamptz as t; - t ---- - t -(1 row) - -SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::date as t; - t ---- - t -(1 row) - -SELECT '4714-11-24 BC'::timestamp < '2020-10-05'::timestamptz as t; - t ---- - t -(1 row) - -SELECT '2020-10-05'::timestamptz >= '4714-11-24 BC'::timestamp as t; - t ---- - t -(1 row) - -RESET TimeZone; --- --- Tests for BETWEEN --- -explain (costs off) -select count(*) from date_tbl - where f1 between '1997-01-01' and '1998-01-01'; - QUERY PLAN ------------------------------------------------------------------------------ - Aggregate - -> Seq Scan on date_tbl - Filter: ((f1 >= '01-01-1997'::date) AND (f1 <= '01-01-1998'::date)) -(3 rows) - -select count(*) from date_tbl - where f1 between '1997-01-01' and '1998-01-01'; - count -------- - 3 -(1 row) - -explain (costs off) -select count(*) from date_tbl - where f1 not between '1997-01-01' and '1998-01-01'; - QUERY PLAN --------------------------------------------------------------------------- - Aggregate - -> Seq Scan on date_tbl - Filter: ((f1 < '01-01-1997'::date) OR (f1 > '01-01-1998'::date)) -(3 rows) - -select count(*) from date_tbl - where f1 not between '1997-01-01' and '1998-01-01'; - count -------- - 13 -(1 row) - -explain (costs off) -select count(*) from date_tbl - where f1 between symmetric '1997-01-01' and '1998-01-01'; - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------- - Aggregate - -> Seq Scan on date_tbl - Filter: (((f1 >= '01-01-1997'::date) AND (f1 <= '01-01-1998'::date)) OR ((f1 >= '01-01-1998'::date) AND (f1 <= '01-01-1997'::date))) -(3 rows) - -select count(*) from date_tbl - where f1 between symmetric '1997-01-01' and '1998-01-01'; - count -------- - 3 -(1 row) - -explain (costs off) -select count(*) from date_tbl - where f1 not between symmetric '1997-01-01' and '1998-01-01'; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ - Aggregate - -> Seq Scan on date_tbl - Filter: (((f1 < '01-01-1997'::date) OR (f1 > '01-01-1998'::date)) AND ((f1 < '01-01-1998'::date) OR (f1 > '01-01-1997'::date))) -(3 rows) - -select count(*) from date_tbl - where f1 not between symmetric '1997-01-01' and '1998-01-01'; - count -------- - 13 -(1 row) - --- --- Formats --- -SET DateStyle TO 'US,Postgres'; -SHOW DateStyle; - DateStyle ---------------- - Postgres, MDY -(1 row) - -SELECT d1 AS us_postgres FROM TIMESTAMP_TBL; - us_postgres ------------------------------ - -infinity - infinity - Thu Jan 01 00:00:00 1970 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:02 1997 - Mon Feb 10 17:32:01.4 1997 - Mon Feb 10 17:32:01.5 1997 - Mon Feb 10 17:32:01.6 1997 - Thu Jan 02 00:00:00 1997 - Thu Jan 02 03:04:05 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Tue Jun 10 17:32:01 1997 - Sat Sep 22 18:19:20 2001 - Wed Mar 15 08:14:01 2000 - Wed Mar 15 13:14:02 2000 - Wed Mar 15 12:14:03 2000 - Wed Mar 15 03:14:04 2000 - Wed Mar 15 02:14:05 2000 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:00 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Mon Feb 10 17:32:01 1997 - Tue Jun 10 18:32:01 1997 - Mon Feb 10 17:32:01 1997 - Tue Feb 11 17:32:01 1997 - Wed Feb 12 17:32:01 1997 - Thu Feb 13 17:32:01 1997 - Fri Feb 14 17:32:01 1997 - Sat Feb 15 17:32:01 1997 - Sun Feb 16 17:32:01 1997 - Tue Feb 16 17:32:01 0097 BC - Sat Feb 16 17:32:01 0097 - Thu Feb 16 17:32:01 0597 - Tue Feb 16 17:32:01 1097 - Sat Feb 16 17:32:01 1697 - Thu Feb 16 17:32:01 1797 - Tue Feb 16 17:32:01 1897 - Sun Feb 16 17:32:01 1997 - Sat Feb 16 17:32:01 2097 - Wed Feb 28 17:32:01 1996 - Thu Feb 29 17:32:01 1996 - Fri Mar 01 17:32:01 1996 - Mon Dec 30 17:32:01 1996 - Tue Dec 31 17:32:01 1996 - Wed Jan 01 17:32:01 1997 - Fri Feb 28 17:32:01 1997 - Sat Mar 01 17:32:01 1997 - Tue Dec 30 17:32:01 1997 - Wed Dec 31 17:32:01 1997 - Fri Dec 31 17:32:01 1999 - Sat Jan 01 17:32:01 2000 - Sun Dec 31 17:32:01 2000 - Mon Jan 01 17:32:01 2001 -(65 rows) - -SET DateStyle TO 'US,ISO'; -SELECT d1 AS us_iso FROM TIMESTAMP_TBL; - us_iso ------------------------- - -infinity - infinity - 1970-01-01 00:00:00 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:02 - 1997-02-10 17:32:01.4 - 1997-02-10 17:32:01.5 - 1997-02-10 17:32:01.6 - 1997-01-02 00:00:00 - 1997-01-02 03:04:05 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-06-10 17:32:01 - 2001-09-22 18:19:20 - 2000-03-15 08:14:01 - 2000-03-15 13:14:02 - 2000-03-15 12:14:03 - 2000-03-15 03:14:04 - 2000-03-15 02:14:05 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:00 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-06-10 18:32:01 - 1997-02-10 17:32:01 - 1997-02-11 17:32:01 - 1997-02-12 17:32:01 - 1997-02-13 17:32:01 - 1997-02-14 17:32:01 - 1997-02-15 17:32:01 - 1997-02-16 17:32:01 - 0097-02-16 17:32:01 BC - 0097-02-16 17:32:01 - 0597-02-16 17:32:01 - 1097-02-16 17:32:01 - 1697-02-16 17:32:01 - 1797-02-16 17:32:01 - 1897-02-16 17:32:01 - 1997-02-16 17:32:01 - 2097-02-16 17:32:01 - 1996-02-28 17:32:01 - 1996-02-29 17:32:01 - 1996-03-01 17:32:01 - 1996-12-30 17:32:01 - 1996-12-31 17:32:01 - 1997-01-01 17:32:01 - 1997-02-28 17:32:01 - 1997-03-01 17:32:01 - 1997-12-30 17:32:01 - 1997-12-31 17:32:01 - 1999-12-31 17:32:01 - 2000-01-01 17:32:01 - 2000-12-31 17:32:01 - 2001-01-01 17:32:01 -(65 rows) - -SET DateStyle TO 'US,SQL'; -SHOW DateStyle; - DateStyle ------------ - SQL, MDY -(1 row) - -SELECT d1 AS us_sql FROM TIMESTAMP_TBL; - us_sql ------------------------- - -infinity - infinity - 01/01/1970 00:00:00 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:02 - 02/10/1997 17:32:01.4 - 02/10/1997 17:32:01.5 - 02/10/1997 17:32:01.6 - 01/02/1997 00:00:00 - 01/02/1997 03:04:05 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 06/10/1997 17:32:01 - 09/22/2001 18:19:20 - 03/15/2000 08:14:01 - 03/15/2000 13:14:02 - 03/15/2000 12:14:03 - 03/15/2000 03:14:04 - 03/15/2000 02:14:05 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:00 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 02/10/1997 17:32:01 - 06/10/1997 18:32:01 - 02/10/1997 17:32:01 - 02/11/1997 17:32:01 - 02/12/1997 17:32:01 - 02/13/1997 17:32:01 - 02/14/1997 17:32:01 - 02/15/1997 17:32:01 - 02/16/1997 17:32:01 - 02/16/0097 17:32:01 BC - 02/16/0097 17:32:01 - 02/16/0597 17:32:01 - 02/16/1097 17:32:01 - 02/16/1697 17:32:01 - 02/16/1797 17:32:01 - 02/16/1897 17:32:01 - 02/16/1997 17:32:01 - 02/16/2097 17:32:01 - 02/28/1996 17:32:01 - 02/29/1996 17:32:01 - 03/01/1996 17:32:01 - 12/30/1996 17:32:01 - 12/31/1996 17:32:01 - 01/01/1997 17:32:01 - 02/28/1997 17:32:01 - 03/01/1997 17:32:01 - 12/30/1997 17:32:01 - 12/31/1997 17:32:01 - 12/31/1999 17:32:01 - 01/01/2000 17:32:01 - 12/31/2000 17:32:01 - 01/01/2001 17:32:01 -(65 rows) - -SET DateStyle TO 'European,Postgres'; -SHOW DateStyle; - DateStyle ---------------- - Postgres, DMY -(1 row) - -INSERT INTO TIMESTAMP_TBL VALUES('13/06/1957'); -SELECT count(*) as one FROM TIMESTAMP_TBL WHERE d1 = 'Jun 13 1957'; - one ------ - 1 -(1 row) - -SELECT d1 AS european_postgres FROM TIMESTAMP_TBL; - european_postgres ------------------------------ - -infinity - infinity - Thu 01 Jan 00:00:00 1970 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:02 1997 - Mon 10 Feb 17:32:01.4 1997 - Mon 10 Feb 17:32:01.5 1997 - Mon 10 Feb 17:32:01.6 1997 - Thu 02 Jan 00:00:00 1997 - Thu 02 Jan 03:04:05 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Tue 10 Jun 17:32:01 1997 - Sat 22 Sep 18:19:20 2001 - Wed 15 Mar 08:14:01 2000 - Wed 15 Mar 13:14:02 2000 - Wed 15 Mar 12:14:03 2000 - Wed 15 Mar 03:14:04 2000 - Wed 15 Mar 02:14:05 2000 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:00 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Tue 10 Jun 18:32:01 1997 - Mon 10 Feb 17:32:01 1997 - Tue 11 Feb 17:32:01 1997 - Wed 12 Feb 17:32:01 1997 - Thu 13 Feb 17:32:01 1997 - Fri 14 Feb 17:32:01 1997 - Sat 15 Feb 17:32:01 1997 - Sun 16 Feb 17:32:01 1997 - Tue 16 Feb 17:32:01 0097 BC - Sat 16 Feb 17:32:01 0097 - Thu 16 Feb 17:32:01 0597 - Tue 16 Feb 17:32:01 1097 - Sat 16 Feb 17:32:01 1697 - Thu 16 Feb 17:32:01 1797 - Tue 16 Feb 17:32:01 1897 - Sun 16 Feb 17:32:01 1997 - Sat 16 Feb 17:32:01 2097 - Wed 28 Feb 17:32:01 1996 - Thu 29 Feb 17:32:01 1996 - Fri 01 Mar 17:32:01 1996 - Mon 30 Dec 17:32:01 1996 - Tue 31 Dec 17:32:01 1996 - Wed 01 Jan 17:32:01 1997 - Fri 28 Feb 17:32:01 1997 - Sat 01 Mar 17:32:01 1997 - Tue 30 Dec 17:32:01 1997 - Wed 31 Dec 17:32:01 1997 - Fri 31 Dec 17:32:01 1999 - Sat 01 Jan 17:32:01 2000 - Sun 31 Dec 17:32:01 2000 - Mon 01 Jan 17:32:01 2001 - Thu 13 Jun 00:00:00 1957 -(66 rows) - -SET DateStyle TO 'European,ISO'; -SHOW DateStyle; - DateStyle ------------ - ISO, DMY -(1 row) - -SELECT d1 AS european_iso FROM TIMESTAMP_TBL; - european_iso ------------------------- - -infinity - infinity - 1970-01-01 00:00:00 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:02 - 1997-02-10 17:32:01.4 - 1997-02-10 17:32:01.5 - 1997-02-10 17:32:01.6 - 1997-01-02 00:00:00 - 1997-01-02 03:04:05 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-06-10 17:32:01 - 2001-09-22 18:19:20 - 2000-03-15 08:14:01 - 2000-03-15 13:14:02 - 2000-03-15 12:14:03 - 2000-03-15 03:14:04 - 2000-03-15 02:14:05 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:00 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-02-10 17:32:01 - 1997-06-10 18:32:01 - 1997-02-10 17:32:01 - 1997-02-11 17:32:01 - 1997-02-12 17:32:01 - 1997-02-13 17:32:01 - 1997-02-14 17:32:01 - 1997-02-15 17:32:01 - 1997-02-16 17:32:01 - 0097-02-16 17:32:01 BC - 0097-02-16 17:32:01 - 0597-02-16 17:32:01 - 1097-02-16 17:32:01 - 1697-02-16 17:32:01 - 1797-02-16 17:32:01 - 1897-02-16 17:32:01 - 1997-02-16 17:32:01 - 2097-02-16 17:32:01 - 1996-02-28 17:32:01 - 1996-02-29 17:32:01 - 1996-03-01 17:32:01 - 1996-12-30 17:32:01 - 1996-12-31 17:32:01 - 1997-01-01 17:32:01 - 1997-02-28 17:32:01 - 1997-03-01 17:32:01 - 1997-12-30 17:32:01 - 1997-12-31 17:32:01 - 1999-12-31 17:32:01 - 2000-01-01 17:32:01 - 2000-12-31 17:32:01 - 2001-01-01 17:32:01 - 1957-06-13 00:00:00 -(66 rows) - -SET DateStyle TO 'European,SQL'; -SHOW DateStyle; - DateStyle ------------ - SQL, DMY -(1 row) - -SELECT d1 AS european_sql FROM TIMESTAMP_TBL; - european_sql ------------------------- - -infinity - infinity - 01/01/1970 00:00:00 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:02 - 10/02/1997 17:32:01.4 - 10/02/1997 17:32:01.5 - 10/02/1997 17:32:01.6 - 02/01/1997 00:00:00 - 02/01/1997 03:04:05 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/06/1997 17:32:01 - 22/09/2001 18:19:20 - 15/03/2000 08:14:01 - 15/03/2000 13:14:02 - 15/03/2000 12:14:03 - 15/03/2000 03:14:04 - 15/03/2000 02:14:05 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:00 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/02/1997 17:32:01 - 10/06/1997 18:32:01 - 10/02/1997 17:32:01 - 11/02/1997 17:32:01 - 12/02/1997 17:32:01 - 13/02/1997 17:32:01 - 14/02/1997 17:32:01 - 15/02/1997 17:32:01 - 16/02/1997 17:32:01 - 16/02/0097 17:32:01 BC - 16/02/0097 17:32:01 - 16/02/0597 17:32:01 - 16/02/1097 17:32:01 - 16/02/1697 17:32:01 - 16/02/1797 17:32:01 - 16/02/1897 17:32:01 - 16/02/1997 17:32:01 - 16/02/2097 17:32:01 - 28/02/1996 17:32:01 - 29/02/1996 17:32:01 - 01/03/1996 17:32:01 - 30/12/1996 17:32:01 - 31/12/1996 17:32:01 - 01/01/1997 17:32:01 - 28/02/1997 17:32:01 - 01/03/1997 17:32:01 - 30/12/1997 17:32:01 - 31/12/1997 17:32:01 - 31/12/1999 17:32:01 - 01/01/2000 17:32:01 - 31/12/2000 17:32:01 - 01/01/2001 17:32:01 - 13/06/1957 00:00:00 -(66 rows) - -RESET DateStyle; --- --- to_timestamp() --- -SELECT to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); - to_timestamp ------------------------------- - Sat Feb 16 08:14:30 0097 LMT -(1 row) - -SELECT to_timestamp('97/2/16 8:14:30', 'FMYYYY/FMMM/FMDD FMHH:FMMI:FMSS'); - to_timestamp ------------------------------- - Sat Feb 16 08:14:30 0097 LMT -(1 row) - -SELECT to_timestamp('2011$03!18 23_38_15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Fri Mar 18 23:38:15 2011 PDT -(1 row) - -SELECT to_timestamp('1985 January 12', 'YYYY FMMonth DD'); - to_timestamp ------------------------------- - Sat Jan 12 00:00:00 1985 PST -(1 row) - -SELECT to_timestamp('1985 FMMonth 12', 'YYYY "FMMonth" DD'); - to_timestamp ------------------------------- - Sat Jan 12 00:00:00 1985 PST -(1 row) - -SELECT to_timestamp('1985 \ 12', 'YYYY \\ DD'); - to_timestamp ------------------------------- - Sat Jan 12 00:00:00 1985 PST -(1 row) - -SELECT to_timestamp('My birthday-> Year: 1976, Month: May, Day: 16', - '"My birthday-> Year:" YYYY, "Month:" FMMonth, "Day:" DD'); - to_timestamp ------------------------------- - Sun May 16 00:00:00 1976 PDT -(1 row) - -SELECT to_timestamp('1,582nd VIII 21', 'Y,YYYth FMRM DD'); - to_timestamp ------------------------------- - Sat Aug 21 00:00:00 1582 LMT -(1 row) - -SELECT to_timestamp('15 "text between quote marks" 98 54 45', - E'HH24 "\\"text between quote marks\\"" YY MI SS'); - to_timestamp ------------------------------- - Thu Jan 01 15:54:45 1998 PST -(1 row) - -SELECT to_timestamp('05121445482000', 'MMDDHH24MISSYYYY'); - to_timestamp ------------------------------- - Fri May 12 14:45:48 2000 PDT -(1 row) - -SELECT to_timestamp('2000January09Sunday', 'YYYYFMMonthDDFMDay'); - to_timestamp ------------------------------- - Sun Jan 09 00:00:00 2000 PST -(1 row) - -SELECT to_timestamp('97/Feb/16', 'YYMonDD'); -ERROR: invalid value "/Feb/16" for "Mon" -DETAIL: The given value did not match any of the allowed values for this field. -SELECT to_timestamp('97/Feb/16', 'YY:Mon:DD'); - to_timestamp ------------------------------- - Sun Feb 16 00:00:00 1997 PST -(1 row) - -SELECT to_timestamp('97/Feb/16', 'FXYY:Mon:DD'); - to_timestamp ------------------------------- - Sun Feb 16 00:00:00 1997 PST -(1 row) - -SELECT to_timestamp('97/Feb/16', 'FXYY/Mon/DD'); - to_timestamp ------------------------------- - Sun Feb 16 00:00:00 1997 PST -(1 row) - -SELECT to_timestamp('19971116', 'YYYYMMDD'); - to_timestamp ------------------------------- - Sun Nov 16 00:00:00 1997 PST -(1 row) - -SELECT to_timestamp('20000-1116', 'YYYY-MMDD'); - to_timestamp -------------------------------- - Thu Nov 16 00:00:00 20000 PST -(1 row) - -SELECT to_timestamp('1997 AD 11 16', 'YYYY BC MM DD'); - to_timestamp ------------------------------- - Sun Nov 16 00:00:00 1997 PST -(1 row) - -SELECT to_timestamp('1997 BC 11 16', 'YYYY BC MM DD'); - to_timestamp ---------------------------------- - Tue Nov 16 00:00:00 1997 LMT BC -(1 row) - -SELECT to_timestamp('1997 A.D. 11 16', 'YYYY B.C. MM DD'); - to_timestamp ------------------------------- - Sun Nov 16 00:00:00 1997 PST -(1 row) - -SELECT to_timestamp('1997 B.C. 11 16', 'YYYY B.C. MM DD'); - to_timestamp ---------------------------------- - Tue Nov 16 00:00:00 1997 LMT BC -(1 row) - -SELECT to_timestamp('9-1116', 'Y-MMDD'); - to_timestamp ------------------------------- - Mon Nov 16 00:00:00 2009 PST -(1 row) - -SELECT to_timestamp('95-1116', 'YY-MMDD'); - to_timestamp ------------------------------- - Thu Nov 16 00:00:00 1995 PST -(1 row) - -SELECT to_timestamp('995-1116', 'YYY-MMDD'); - to_timestamp ------------------------------- - Thu Nov 16 00:00:00 1995 PST -(1 row) - -SELECT to_timestamp('2005426', 'YYYYWWD'); - to_timestamp ------------------------------- - Sat Oct 15 00:00:00 2005 PDT -(1 row) - -SELECT to_timestamp('2005300', 'YYYYDDD'); - to_timestamp ------------------------------- - Thu Oct 27 00:00:00 2005 PDT -(1 row) - -SELECT to_timestamp('2005527', 'IYYYIWID'); - to_timestamp ------------------------------- - Sun Jan 01 00:00:00 2006 PST -(1 row) - -SELECT to_timestamp('005527', 'IYYIWID'); - to_timestamp ------------------------------- - Sun Jan 01 00:00:00 2006 PST -(1 row) - -SELECT to_timestamp('05527', 'IYIWID'); - to_timestamp ------------------------------- - Sun Jan 01 00:00:00 2006 PST -(1 row) - -SELECT to_timestamp('5527', 'IIWID'); - to_timestamp ------------------------------- - Sun Jan 01 00:00:00 2006 PST -(1 row) - -SELECT to_timestamp('2005364', 'IYYYIDDD'); - to_timestamp ------------------------------- - Sun Jan 01 00:00:00 2006 PST -(1 row) - -SELECT to_timestamp('20050302', 'YYYYMMDD'); - to_timestamp ------------------------------- - Wed Mar 02 00:00:00 2005 PST -(1 row) - -SELECT to_timestamp('2005 03 02', 'YYYYMMDD'); - to_timestamp ------------------------------- - Wed Mar 02 00:00:00 2005 PST -(1 row) - -SELECT to_timestamp(' 2005 03 02', 'YYYYMMDD'); - to_timestamp ------------------------------- - Wed Mar 02 00:00:00 2005 PST -(1 row) - -SELECT to_timestamp(' 20050302', 'YYYYMMDD'); - to_timestamp ------------------------------- - Wed Mar 02 00:00:00 2005 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 AM', 'YYYY-MM-DD HH12:MI PM'); - to_timestamp ------------------------------- - Sun Dec 18 11:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 PM', 'YYYY-MM-DD HH12:MI PM'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 A.M.', 'YYYY-MM-DD HH12:MI P.M.'); - to_timestamp ------------------------------- - Sun Dec 18 11:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 P.M.', 'YYYY-MM-DD HH12:MI P.M.'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 +05', 'YYYY-MM-DD HH12:MI TZH'); - to_timestamp ------------------------------- - Sat Dec 17 22:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZH'); - to_timestamp ------------------------------- - Sun Dec 18 08:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 +05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); - to_timestamp ------------------------------- - Sat Dec 17 22:18:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 -05:20', 'YYYY-MM-DD HH12:MI TZH:TZM'); - to_timestamp ------------------------------- - Sun Dec 18 08:58:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 20', 'YYYY-MM-DD HH12:MI TZM'); - to_timestamp ------------------------------- - Sun Dec 18 03:18:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 EST', 'YYYY-MM-DD HH12:MI TZ'); - to_timestamp ------------------------------- - Sun Dec 18 08:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI TZ'); - to_timestamp ------------------------------- - Sun Dec 18 08:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 +01:30', 'YYYY-MM-DD HH12:MI TZ'); - to_timestamp ------------------------------- - Sun Dec 18 02:08:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 MSK', 'YYYY-MM-DD HH12:MI TZ'); -- dyntz - to_timestamp ------------------------------- - Sat Dec 17 23:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 00:00 LMT', 'YYYY-MM-DD HH24:MI TZ'); -- dyntz - to_timestamp ------------------------------- - Sat Dec 17 23:52:58 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38ESTFOO24', 'YYYY-MM-DD HH12:MITZFOOSS'); - to_timestamp ------------------------------- - Sun Dec 18 08:38:24 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38-05FOO24', 'YYYY-MM-DD HH12:MITZFOOSS'); - to_timestamp ------------------------------- - Sun Dec 18 08:38:24 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 JUNK', 'YYYY-MM-DD HH12:MI TZ'); -- error -ERROR: invalid value "JUNK" for "TZ" -DETAIL: Time zone abbreviation is not recognized. -SELECT to_timestamp('2011-12-18 11:38 ...', 'YYYY-MM-DD HH12:MI TZ'); -- error -ERROR: invalid value ".." for "TZ" -DETAIL: Value must be an integer. -SELECT to_timestamp('2011-12-18 11:38 -05', 'YYYY-MM-DD HH12:MI OF'); - to_timestamp ------------------------------- - Sun Dec 18 08:38:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 +01:30', 'YYYY-MM-DD HH12:MI OF'); - to_timestamp ------------------------------- - Sun Dec 18 02:08:00 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 11:38 +xyz', 'YYYY-MM-DD HH12:MI OF'); -- error -ERROR: invalid value "xy" for "OF" -DETAIL: Value must be an integer. -SELECT to_timestamp('2011-12-18 11:38 +01:xyz', 'YYYY-MM-DD HH12:MI OF'); -- error -ERROR: invalid value "xy" for "OF" -DETAIL: Value must be an integer. -SELECT to_timestamp('2018-11-02 12:34:56.025', 'YYYY-MM-DD HH24:MI:SS.MS'); - to_timestamp ----------------------------------- - Fri Nov 02 12:34:56.025 2018 PDT -(1 row) - -SELECT i, to_timestamp('2018-11-02 12:34:56', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+------------------------------ - 1 | Fri Nov 02 12:34:56 2018 PDT - 2 | Fri Nov 02 12:34:56 2018 PDT - 3 | Fri Nov 02 12:34:56 2018 PDT - 4 | Fri Nov 02 12:34:56 2018 PDT - 5 | Fri Nov 02 12:34:56 2018 PDT - 6 | Fri Nov 02 12:34:56 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.1', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+-------------------------------- - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.1 2018 PDT - 3 | Fri Nov 02 12:34:56.1 2018 PDT - 4 | Fri Nov 02 12:34:56.1 2018 PDT - 5 | Fri Nov 02 12:34:56.1 2018 PDT - 6 | Fri Nov 02 12:34:56.1 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.12', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+--------------------------------- - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.12 2018 PDT - 3 | Fri Nov 02 12:34:56.12 2018 PDT - 4 | Fri Nov 02 12:34:56.12 2018 PDT - 5 | Fri Nov 02 12:34:56.12 2018 PDT - 6 | Fri Nov 02 12:34:56.12 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.123', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+---------------------------------- - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.12 2018 PDT - 3 | Fri Nov 02 12:34:56.123 2018 PDT - 4 | Fri Nov 02 12:34:56.123 2018 PDT - 5 | Fri Nov 02 12:34:56.123 2018 PDT - 6 | Fri Nov 02 12:34:56.123 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.1234', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+----------------------------------- - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.12 2018 PDT - 3 | Fri Nov 02 12:34:56.123 2018 PDT - 4 | Fri Nov 02 12:34:56.1234 2018 PDT - 5 | Fri Nov 02 12:34:56.1234 2018 PDT - 6 | Fri Nov 02 12:34:56.1234 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.12345', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+------------------------------------ - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.12 2018 PDT - 3 | Fri Nov 02 12:34:56.123 2018 PDT - 4 | Fri Nov 02 12:34:56.1235 2018 PDT - 5 | Fri Nov 02 12:34:56.12345 2018 PDT - 6 | Fri Nov 02 12:34:56.12345 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+------------------------------------- - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.12 2018 PDT - 3 | Fri Nov 02 12:34:56.123 2018 PDT - 4 | Fri Nov 02 12:34:56.1235 2018 PDT - 5 | Fri Nov 02 12:34:56.12346 2018 PDT - 6 | Fri Nov 02 12:34:56.123456 2018 PDT -(6 rows) - -SELECT i, to_timestamp('2018-11-02 12:34:56.123456789', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; -ERROR: date/time field value out of range: "2018-11-02 12:34:56.123456789" -SELECT i, to_timestamp('20181102123456123456', 'YYYYMMDDHH24MISSFF' || i) FROM generate_series(1, 6) i; - i | to_timestamp ----+------------------------------------- - 1 | Fri Nov 02 12:34:56.1 2018 PDT - 2 | Fri Nov 02 12:34:56.12 2018 PDT - 3 | Fri Nov 02 12:34:56.123 2018 PDT - 4 | Fri Nov 02 12:34:56.1235 2018 PDT - 5 | Fri Nov 02 12:34:56.12346 2018 PDT - 6 | Fri Nov 02 12:34:56.123456 2018 PDT -(6 rows) - -SELECT to_date('1 4 1902', 'Q MM YYYY'); -- Q is ignored - to_date ------------- - 04-01-1902 -(1 row) - -SELECT to_date('3 4 21 01', 'W MM CC YY'); - to_date ------------- - 04-15-2001 -(1 row) - -SELECT to_date('2458872', 'J'); - to_date ------------- - 01-23-2020 -(1 row) - --- --- Check handling of BC dates --- -SELECT to_date('44-02-01 BC','YYYY-MM-DD BC'); - to_date ---------------- - 02-01-0044 BC -(1 row) - -SELECT to_date('-44-02-01','YYYY-MM-DD'); - to_date ---------------- - 02-01-0044 BC -(1 row) - -SELECT to_date('-44-02-01 BC','YYYY-MM-DD BC'); - to_date ------------- - 02-01-0044 -(1 row) - -SELECT to_timestamp('44-02-01 11:12:13 BC','YYYY-MM-DD HH24:MI:SS BC'); - to_timestamp ---------------------------------- - Fri Feb 01 11:12:13 0044 LMT BC -(1 row) - -SELECT to_timestamp('-44-02-01 11:12:13','YYYY-MM-DD HH24:MI:SS'); - to_timestamp ---------------------------------- - Fri Feb 01 11:12:13 0044 LMT BC -(1 row) - -SELECT to_timestamp('-44-02-01 11:12:13 BC','YYYY-MM-DD HH24:MI:SS BC'); - to_timestamp ------------------------------- - Mon Feb 01 11:12:13 0044 LMT -(1 row) - --- --- Check handling of multiple spaces in format and/or input --- -SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:15 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:15 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:15 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:15 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:15 2011 PST -(1 row) - -SELECT to_timestamp('2011-12-18 23:38:15', 'YYYY-MM-DD HH24:MI:SS'); - to_timestamp ------------------------------- - Sun Dec 18 23:38:15 2011 PST -(1 row) - -SELECT to_timestamp('2000+ JUN', 'YYYY/MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp(' 2000 +JUN', 'YYYY/MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp(' 2000 +JUN', 'YYYY//MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp('2000 +JUN', 'YYYY//MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp('2000 + JUN', 'YYYY MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp('2000 ++ JUN', 'YYYY MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp('2000 + + JUN', 'YYYY MON'); -ERROR: invalid value "+" for "MON" -DETAIL: The given value did not match any of the allowed values for this field. -SELECT to_timestamp('2000 + + JUN', 'YYYY MON'); - to_timestamp ------------------------------- - Thu Jun 01 00:00:00 2000 PDT -(1 row) - -SELECT to_timestamp('2000 -10', 'YYYY TZH'); - to_timestamp ------------------------------- - Sat Jan 01 02:00:00 2000 PST -(1 row) - -SELECT to_timestamp('2000 -10', 'YYYY TZH'); - to_timestamp ------------------------------- - Fri Dec 31 06:00:00 1999 PST -(1 row) - -SELECT to_date('2011 12 18', 'YYYY MM DD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 12 18', 'YYYY MM DD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 12 18', 'YYYY MM DD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 12 18', 'YYYY MM DD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 12 18', 'YYYY MM DD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 12 18', 'YYYY MM DD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 12 18', 'YYYYxMMxDD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011x 12x 18', 'YYYYxMMxDD'); - to_date ------------- - 12-18-2011 -(1 row) - -SELECT to_date('2011 x12 x18', 'YYYYxMMxDD'); -ERROR: invalid value "x1" for "MM" -DETAIL: Value must be an integer. --- --- Check errors for some incorrect usages of to_timestamp() and to_date() --- --- Mixture of date conventions (ISO week and Gregorian): -SELECT to_timestamp('2005527', 'YYYYIWID'); -ERROR: invalid combination of date conventions -HINT: Do not mix Gregorian and ISO week date conventions in a formatting template. --- Insufficient characters in the source string: -SELECT to_timestamp('19971', 'YYYYMMDD'); -ERROR: source string too short for "MM" formatting field -DETAIL: Field requires 2 characters, but only 1 remain. -HINT: If your source string is not fixed-width, try using the "FM" modifier. --- Insufficient digit characters for a single node: -SELECT to_timestamp('19971)24', 'YYYYMMDD'); -ERROR: invalid value "1)" for "MM" -DETAIL: Field requires 2 characters, but only 1 could be parsed. -HINT: If your source string is not fixed-width, try using the "FM" modifier. --- We don't accept full-length day or month names if short form is specified: -SELECT to_timestamp('Friday 1-January-1999', 'DY DD MON YYYY'); -ERROR: invalid value "da" for "DD" -DETAIL: Value must be an integer. -SELECT to_timestamp('Fri 1-January-1999', 'DY DD MON YYYY'); -ERROR: invalid value "uary" for "YYYY" -DETAIL: Value must be an integer. -SELECT to_timestamp('Fri 1-Jan-1999', 'DY DD MON YYYY'); -- ok - to_timestamp ------------------------------- - Fri Jan 01 00:00:00 1999 PST -(1 row) - --- Value clobbering: -SELECT to_timestamp('1997-11-Jan-16', 'YYYY-MM-Mon-DD'); -ERROR: conflicting values for "Mon" field in formatting string -DETAIL: This value contradicts a previous setting for the same field type. --- Non-numeric input: -SELECT to_timestamp('199711xy', 'YYYYMMDD'); -ERROR: invalid value "xy" for "DD" -DETAIL: Value must be an integer. --- Input that doesn't fit in an int: -SELECT to_timestamp('10000000000', 'FMYYYY'); -ERROR: value for "YYYY" in source string is out of range -DETAIL: Value must be in the range -2147483648 to 2147483647. --- Out-of-range and not-quite-out-of-range fields: -SELECT to_timestamp('2016-06-13 25:00:00', 'YYYY-MM-DD HH24:MI:SS'); -ERROR: date/time field value out of range: "2016-06-13 25:00:00" -SELECT to_timestamp('2016-06-13 15:60:00', 'YYYY-MM-DD HH24:MI:SS'); -ERROR: date/time field value out of range: "2016-06-13 15:60:00" -SELECT to_timestamp('2016-06-13 15:50:60', 'YYYY-MM-DD HH24:MI:SS'); -ERROR: date/time field value out of range: "2016-06-13 15:50:60" -SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -- ok - to_timestamp ------------------------------- - Mon Jun 13 15:50:55 2016 PDT -(1 row) - -SELECT to_timestamp('2016-06-13 15:50:55', 'YYYY-MM-DD HH:MI:SS'); -ERROR: hour "15" is invalid for the 12-hour clock -HINT: Use the 24-hour clock, or give an hour between 1 and 12. -SELECT to_timestamp('2016-13-01 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -ERROR: date/time field value out of range: "2016-13-01 15:50:55" -SELECT to_timestamp('2016-02-30 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -ERROR: date/time field value out of range: "2016-02-30 15:50:55" -SELECT to_timestamp('2016-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -- ok - to_timestamp ------------------------------- - Mon Feb 29 15:50:55 2016 PST -(1 row) - -SELECT to_timestamp('2015-02-29 15:50:55', 'YYYY-MM-DD HH24:MI:SS'); -ERROR: date/time field value out of range: "2015-02-29 15:50:55" -SELECT to_timestamp('2015-02-11 86000', 'YYYY-MM-DD SSSS'); -- ok - to_timestamp ------------------------------- - Wed Feb 11 23:53:20 2015 PST -(1 row) - -SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSS'); -ERROR: date/time field value out of range: "2015-02-11 86400" -SELECT to_timestamp('2015-02-11 86000', 'YYYY-MM-DD SSSSS'); -- ok - to_timestamp ------------------------------- - Wed Feb 11 23:53:20 2015 PST -(1 row) - -SELECT to_timestamp('2015-02-11 86400', 'YYYY-MM-DD SSSSS'); -ERROR: date/time field value out of range: "2015-02-11 86400" -SELECT to_timestamp('1000000000,999', 'Y,YYY'); -ERROR: value for "Y,YYY" in source string is out of range -SELECT to_timestamp('0.-2147483648', 'SS.MS'); -ERROR: date/time field value out of range: "0.-2147483648" -SELECT to_timestamp('613566758', 'W'); -ERROR: date/time field value out of range: "613566758" -SELECT to_timestamp('2024 613566758 1', 'YYYY WW D'); -ERROR: date/time field value out of range: "2024 613566758 1" -SELECT to_date('2016-13-10', 'YYYY-MM-DD'); -ERROR: date/time field value out of range: "2016-13-10" -SELECT to_date('2016-02-30', 'YYYY-MM-DD'); -ERROR: date/time field value out of range: "2016-02-30" -SELECT to_date('2016-02-29', 'YYYY-MM-DD'); -- ok - to_date ------------- - 02-29-2016 -(1 row) - -SELECT to_date('2015-02-29', 'YYYY-MM-DD'); -ERROR: date/time field value out of range: "2015-02-29" -SELECT to_date('2015 365', 'YYYY DDD'); -- ok - to_date ------------- - 12-31-2015 -(1 row) - -SELECT to_date('2015 366', 'YYYY DDD'); -ERROR: date/time field value out of range: "2015 366" -SELECT to_date('2016 365', 'YYYY DDD'); -- ok - to_date ------------- - 12-30-2016 -(1 row) - -SELECT to_date('2016 366', 'YYYY DDD'); -- ok - to_date ------------- - 12-31-2016 -(1 row) - -SELECT to_date('2016 367', 'YYYY DDD'); -ERROR: date/time field value out of range: "2016 367" -SELECT to_date('0000-02-01','YYYY-MM-DD'); -- allowed, though it shouldn't be - to_date ---------------- - 02-01-0001 BC -(1 row) - -SELECT to_date('100000000', 'CC'); -ERROR: date/time field value out of range: "100000000" -SELECT to_date('-100000000', 'CC'); -ERROR: date/time field value out of range: "-100000000" -SELECT to_date('-2147483648 01', 'CC YY'); -ERROR: date/time field value out of range: "-2147483648 01" -SELECT to_date('2147483647 01', 'CC YY'); -ERROR: date/time field value out of range: "2147483647 01" --- to_char's TZ format code produces zone abbrev if known -SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); - to_char -------------------------- - 2012-12-12 12:00:00 PST -(1 row) - -SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS tz'); - to_char -------------------------- - 2012-12-12 12:00:00 pst -(1 row) - --- --- Check behavior with SQL-style fixed-GMT-offset time zone (cf bug #8572) --- -SET TIME ZONE 'America/New_York'; -SET TIME ZONE '-1.5'; -SHOW TIME ZONE; - TimeZone ----------------- - <-01:30>+01:30 -(1 row) - -SELECT '2012-12-12 12:00'::timestamptz; - timestamptz ---------------------------------- - Wed Dec 12 12:00:00 2012 -01:30 -(1 row) - -SELECT '2012-12-12 12:00 America/New_York'::timestamptz; - timestamptz ---------------------------------- - Wed Dec 12 15:30:00 2012 -01:30 -(1 row) - -SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); - to_char ----------------------------- - 2012-12-12 12:00:00 -01:30 -(1 row) - -SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD SSSS'); - to_char ------------------- - 2012-12-12 43200 -(1 row) - -SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD SSSSS'); - to_char ------------------- - 2012-12-12 43200 -(1 row) - -SET TIME ZONE '+2'; -SELECT to_char('2012-12-12 12:00'::timestamptz, 'YYYY-MM-DD HH:MI:SS TZ'); - to_char -------------------------- - 2012-12-12 12:00:00 +02 -(1 row) - -RESET TIME ZONE; +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/opr_sanity.out /tmp/cirrus-ci-build/src/test/regress/results/opr_sanity.out --- /tmp/cirrus-ci-build/src/test/regress/expected/opr_sanity.out 2026-03-09 20:33:45.188310808 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/opr_sanity.out 2026-03-09 20:38:47.620090934 +0000 @@ -488,1835 +488,8 @@ FROM pg_type t WHERE t.typarray = proargtypes[array_length(proargtypes, 1)-1]) END != provariadic; - oid | provariadic | proargtypes ------+-------------+------------- -(0 rows) - --- Check that all and only those functions with a variadic type have --- a variadic argument. -SELECT oid::regprocedure, proargmodes, provariadic -FROM pg_proc -WHERE (proargmodes IS NOT NULL AND 'v' = any(proargmodes)) - IS DISTINCT FROM - (provariadic != 0); - oid | proargmodes | provariadic ------+-------------+------------- -(0 rows) - --- Check for prosupport functions with the wrong signature -SELECT p1.oid, p1.proname, p2.oid, p2.proname -FROM pg_proc AS p1, pg_proc AS p2 -WHERE p2.oid = p1.prosupport AND - (p2.prorettype != 'internal'::regtype OR p2.proretset OR p2.pronargs != 1 - OR p2.proargtypes[0] != 'internal'::regtype); - oid | proname | oid | proname ------+---------+-----+--------- -(0 rows) - --- Insist that all built-in pg_proc entries have descriptions -SELECT p1.oid, p1.proname -FROM pg_proc as p1 LEFT JOIN pg_description as d - ON p1.tableoid = d.classoid and p1.oid = d.objoid and d.objsubid = 0 -WHERE d.classoid IS NULL AND p1.oid <= 9999; - oid | proname ------+--------- -(0 rows) - --- List of built-in leakproof functions --- --- Leakproof functions should only be added after carefully --- scrutinizing all possibly executed codepaths for possible --- information leaks. Don't add functions here unless you know what a --- leakproof function is. If unsure, don't mark it as such. --- temporarily disable fancy output, so catalog changes create less diff noise -\a\t -SELECT p1.oid::regprocedure -FROM pg_proc p1 JOIN pg_namespace pn - ON pronamespace = pn.oid -WHERE nspname = 'pg_catalog' AND proleakproof -ORDER BY 1; -boollt(boolean,boolean) -boolgt(boolean,boolean) -booleq(boolean,boolean) -chareq("char","char") -nameeq(name,name) -int2eq(smallint,smallint) -int2lt(smallint,smallint) -int4eq(integer,integer) -int4lt(integer,integer) -texteq(text,text) -xideq(xid,xid) -cideq(cid,cid) -charne("char","char") -charle("char","char") -chargt("char","char") -charge("char","char") -boolne(boolean,boolean) -int4ne(integer,integer) -int2ne(smallint,smallint) -int2gt(smallint,smallint) -int4gt(integer,integer) -int2le(smallint,smallint) -int4le(integer,integer) -int4ge(integer,integer) -int2ge(smallint,smallint) -textne(text,text) -int24eq(smallint,integer) -int42eq(integer,smallint) -int24lt(smallint,integer) -int42lt(integer,smallint) -int24gt(smallint,integer) -int42gt(integer,smallint) -int24ne(smallint,integer) -int42ne(integer,smallint) -int24le(smallint,integer) -int42le(integer,smallint) -int24ge(smallint,integer) -int42ge(integer,smallint) -oideq(oid,oid) -oidne(oid,oid) -float8(smallint) -float4(smallint) -nameeqtext(name,text) -namelttext(name,text) -nameletext(name,text) -namegetext(name,text) -namegttext(name,text) -namenetext(name,text) -btnametextcmp(name,text) -texteqname(text,name) -textltname(text,name) -textlename(text,name) -textgename(text,name) -textgtname(text,name) -textnename(text,name) -bttextnamecmp(text,name) -float4eq(real,real) -float4ne(real,real) -float4lt(real,real) -float4le(real,real) -float4gt(real,real) -float4ge(real,real) -float8eq(double precision,double precision) -float8ne(double precision,double precision) -float8lt(double precision,double precision) -float8le(double precision,double precision) -float8gt(double precision,double precision) -float8ge(double precision,double precision) -float48eq(real,double precision) -float48ne(real,double precision) -float48lt(real,double precision) -float48le(real,double precision) -float48gt(real,double precision) -float48ge(real,double precision) -float84eq(double precision,real) -float84ne(double precision,real) -float84lt(double precision,real) -float84le(double precision,real) -float84gt(double precision,real) -float84ge(double precision,real) -float8(real) -int4(smallint) -float8(integer) -float4(integer) -btint2cmp(smallint,smallint) -btint4cmp(integer,integer) -btfloat4cmp(real,real) -btfloat8cmp(double precision,double precision) -btoidcmp(oid,oid) -btcharcmp("char","char") -btnamecmp(name,name) -bttextcmp(text,text) -cash_cmp(money,money) -btoidvectorcmp(oidvector,oidvector) -text(name) -name(text) -name(character) -text_larger(text,text) -text_smaller(text,text) -int8eq(bigint,bigint) -int8ne(bigint,bigint) -int8lt(bigint,bigint) -int8gt(bigint,bigint) -int8le(bigint,bigint) -int8ge(bigint,bigint) -int84eq(bigint,integer) -int84ne(bigint,integer) -int84lt(bigint,integer) -int84gt(bigint,integer) -int84le(bigint,integer) -int84ge(bigint,integer) -int8(integer) -float8(bigint) -oidvectorne(oidvector,oidvector) -float4(bigint) -namelt(name,name) -namele(name,name) -namegt(name,name) -namege(name,name) -namene(name,name) -oidvectorlt(oidvector,oidvector) -oidvectorle(oidvector,oidvector) -oidvectoreq(oidvector,oidvector) -oidvectorge(oidvector,oidvector) -oidvectorgt(oidvector,oidvector) -oidlt(oid,oid) -oidle(oid,oid) -text_lt(text,text) -text_le(text,text) -text_gt(text,text) -text_ge(text,text) -int8(smallint) -macaddr_eq(macaddr,macaddr) -macaddr_lt(macaddr,macaddr) -macaddr_le(macaddr,macaddr) -macaddr_gt(macaddr,macaddr) -macaddr_ge(macaddr,macaddr) -macaddr_ne(macaddr,macaddr) -macaddr_cmp(macaddr,macaddr) -btint8cmp(bigint,bigint) -int48eq(integer,bigint) -int48ne(integer,bigint) -int48lt(integer,bigint) -int48gt(integer,bigint) -int48le(integer,bigint) -int48ge(integer,bigint) -cash_eq(money,money) -cash_ne(money,money) -cash_lt(money,money) -cash_le(money,money) -cash_gt(money,money) -cash_ge(money,money) -network_eq(inet,inet) -network_lt(inet,inet) -network_le(inet,inet) -network_gt(inet,inet) -network_ge(inet,inet) -network_ne(inet,inet) -network_cmp(inet,inet) -lseg_eq(lseg,lseg) -bpchareq(character,character) -bpcharlt(character,character) -bpcharle(character,character) -bpchargt(character,character) -bpcharge(character,character) -bpcharne(character,character) -bpchar_larger(character,character) -bpchar_smaller(character,character) -bpcharcmp(character,character) -date_eq(date,date) -date_lt(date,date) -date_le(date,date) -date_gt(date,date) -date_ge(date,date) -date_ne(date,date) -date_cmp(date,date) -time_lt(time without time zone,time without time zone) -time_le(time without time zone,time without time zone) -time_gt(time without time zone,time without time zone) -time_ge(time without time zone,time without time zone) -time_ne(time without time zone,time without time zone) -time_cmp(time without time zone,time without time zone) -time_eq(time without time zone,time without time zone) -timestamptz_eq(timestamp with time zone,timestamp with time zone) -timestamptz_ne(timestamp with time zone,timestamp with time zone) -timestamptz_lt(timestamp with time zone,timestamp with time zone) -timestamptz_le(timestamp with time zone,timestamp with time zone) -timestamptz_ge(timestamp with time zone,timestamp with time zone) -timestamptz_gt(timestamp with time zone,timestamp with time zone) -interval_eq(interval,interval) -interval_ne(interval,interval) -interval_lt(interval,interval) -interval_le(interval,interval) -interval_ge(interval,interval) -interval_gt(interval,interval) -charlt("char","char") -tidne(tid,tid) -int8(oid) -tideq(tid,tid) -timestamptz_cmp(timestamp with time zone,timestamp with time zone) -interval_cmp(interval,interval) -xideqint4(xid,integer) -timetz_eq(time with time zone,time with time zone) -timetz_ne(time with time zone,time with time zone) -timetz_lt(time with time zone,time with time zone) -timetz_le(time with time zone,time with time zone) -timetz_ge(time with time zone,time with time zone) -timetz_gt(time with time zone,time with time zone) -timetz_cmp(time with time zone,time with time zone) -"interval"(time without time zone) -name(character varying) -"varchar"(name) -circle_eq(circle,circle) -circle_ne(circle,circle) -circle_lt(circle,circle) -circle_gt(circle,circle) -circle_le(circle,circle) -circle_ge(circle,circle) -lseg_ne(lseg,lseg) -lseg_lt(lseg,lseg) -lseg_le(lseg,lseg) -lseg_gt(lseg,lseg) -lseg_ge(lseg,lseg) -biteq(bit,bit) -bitne(bit,bit) -bitge(bit,bit) -bitgt(bit,bit) -bitle(bit,bit) -bitlt(bit,bit) -bitcmp(bit,bit) -oidgt(oid,oid) -oidge(oid,oid) -varbiteq(bit varying,bit varying) -varbitne(bit varying,bit varying) -varbitge(bit varying,bit varying) -varbitgt(bit varying,bit varying) -varbitle(bit varying,bit varying) -varbitlt(bit varying,bit varying) -varbitcmp(bit varying,bit varying) -boolle(boolean,boolean) -boolge(boolean,boolean) -btboolcmp(boolean,boolean) -"numeric"(integer) -"numeric"(real) -"numeric"(double precision) -"numeric"(bigint) -"numeric"(smallint) -int28eq(smallint,bigint) -int28ne(smallint,bigint) -int28lt(smallint,bigint) -int28gt(smallint,bigint) -int28le(smallint,bigint) -int28ge(smallint,bigint) -int82eq(bigint,smallint) -int82ne(bigint,smallint) -int82lt(bigint,smallint) -int82gt(bigint,smallint) -int82le(bigint,smallint) -int82ge(bigint,smallint) -byteaeq(bytea,bytea) -bytealt(bytea,bytea) -byteale(bytea,bytea) -byteagt(bytea,bytea) -byteage(bytea,bytea) -byteane(bytea,bytea) -byteacmp(bytea,bytea) -timestamp_cmp(timestamp without time zone,timestamp without time zone) -timestamp_eq(timestamp without time zone,timestamp without time zone) -timestamp_ne(timestamp without time zone,timestamp without time zone) -timestamp_lt(timestamp without time zone,timestamp without time zone) -timestamp_le(timestamp without time zone,timestamp without time zone) -timestamp_ge(timestamp without time zone,timestamp without time zone) -timestamp_gt(timestamp without time zone,timestamp without time zone) -text_pattern_lt(text,text) -text_pattern_le(text,text) -text_pattern_ge(text,text) -text_pattern_gt(text,text) -bttext_pattern_cmp(text,text) -bpchar_pattern_lt(character,character) -bpchar_pattern_le(character,character) -bpchar_pattern_ge(character,character) -bpchar_pattern_gt(character,character) -btbpchar_pattern_cmp(character,character) -btint48cmp(integer,bigint) -btint84cmp(bigint,integer) -btint24cmp(smallint,integer) -btint42cmp(integer,smallint) -btint28cmp(smallint,bigint) -btint82cmp(bigint,smallint) -btfloat48cmp(real,double precision) -btfloat84cmp(double precision,real) -md5(text) -md5(bytea) -bool(integer) -int4(boolean) -tidgt(tid,tid) -tidlt(tid,tid) -tidge(tid,tid) -tidle(tid,tid) -bttidcmp(tid,tid) -uuid_lt(uuid,uuid) -uuid_le(uuid,uuid) -uuid_eq(uuid,uuid) -uuid_ge(uuid,uuid) -uuid_gt(uuid,uuid) -uuid_ne(uuid,uuid) -uuid_cmp(uuid,uuid) -pg_lsn_lt(pg_lsn,pg_lsn) -pg_lsn_le(pg_lsn,pg_lsn) -pg_lsn_eq(pg_lsn,pg_lsn) -pg_lsn_ge(pg_lsn,pg_lsn) -pg_lsn_gt(pg_lsn,pg_lsn) -pg_lsn_ne(pg_lsn,pg_lsn) -pg_lsn_cmp(pg_lsn,pg_lsn) -xidneq(xid,xid) -xidneqint4(xid,integer) -sha224(bytea) -sha256(bytea) -sha384(bytea) -sha512(bytea) -starts_with(text,text) -macaddr8_eq(macaddr8,macaddr8) -macaddr8_lt(macaddr8,macaddr8) -macaddr8_le(macaddr8,macaddr8) -macaddr8_gt(macaddr8,macaddr8) -macaddr8_ge(macaddr8,macaddr8) -macaddr8_ne(macaddr8,macaddr8) -macaddr8_cmp(macaddr8,macaddr8) -macaddr8(macaddr) -xid8lt(xid8,xid8) -xid8gt(xid8,xid8) -xid8le(xid8,xid8) -xid8ge(xid8,xid8) -xid8eq(xid8,xid8) -xid8ne(xid8,xid8) -xid8cmp(xid8,xid8) -uuid_extract_timestamp(uuid) -uuid_extract_version(uuid) -crc32(bytea) -crc32c(bytea) -bytea(smallint) -bytea(integer) -bytea(bigint) -bytea_larger(bytea,bytea) -bytea_smaller(bytea,bytea) -oid8eq(oid8,oid8) -oid8ne(oid8,oid8) -oid8lt(oid8,oid8) -oid8le(oid8,oid8) -oid8gt(oid8,oid8) -oid8ge(oid8,oid8) -btoid8cmp(oid8,oid8) --- Check that functions without argument are not marked as leakproof. -SELECT p1.oid::regprocedure -FROM pg_proc p1 JOIN pg_namespace pn - ON pronamespace = pn.oid -WHERE nspname = 'pg_catalog' AND proleakproof AND pronargs = 0 -ORDER BY 1; --- restore normal output mode -\a\t --- List of functions used by libpq's fe-lobj.c --- --- If the output of this query changes, you probably broke libpq. --- lo_initialize() assumes that there will be at most one match for --- each listed name. -select proname, oid from pg_catalog.pg_proc -where proname in ( - 'lo_open', - 'lo_close', - 'lo_creat', - 'lo_create', - 'lo_unlink', - 'lo_lseek', - 'lo_lseek64', - 'lo_tell', - 'lo_tell64', - 'lo_truncate', - 'lo_truncate64', - 'loread', - 'lowrite') -and pronamespace = (select oid from pg_catalog.pg_namespace - where nspname = 'pg_catalog') -order by 1; - proname | oid ----------------+------ - lo_close | 953 - lo_creat | 957 - lo_create | 715 - lo_lseek | 956 - lo_lseek64 | 3170 - lo_open | 952 - lo_tell | 958 - lo_tell64 | 3171 - lo_truncate | 1004 - lo_truncate64 | 3172 - lo_unlink | 964 - loread | 954 - lowrite | 955 -(13 rows) - --- Check that all immutable functions are marked parallel safe -SELECT p1.oid, p1.proname -FROM pg_proc AS p1 -WHERE provolatile = 'i' AND proparallel = 'u'; - oid | proname ------+--------- -(0 rows) - --- **************** pg_cast **************** --- Catch bogus values in pg_cast columns (other than cases detected by --- oidjoins test). -SELECT * -FROM pg_cast c -WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i') - OR castmethod NOT IN ('f', 'b' ,'i'); - oid | castsource | casttarget | castfunc | castcontext | castmethod ------+------------+------------+----------+-------------+------------ -(0 rows) - --- Check that castfunc is nonzero only for cast methods that need a function, --- and zero otherwise -SELECT * -FROM pg_cast c -WHERE (castmethod = 'f' AND castfunc = 0) - OR (castmethod IN ('b', 'i') AND castfunc <> 0); - oid | castsource | casttarget | castfunc | castcontext | castmethod ------+------------+------------+----------+-------------+------------ -(0 rows) - --- Look for casts to/from the same type that aren't length coercion functions. --- (We assume they are length coercions if they take multiple arguments.) --- Such entries are not necessarily harmful, but they are useless. -SELECT * -FROM pg_cast c -WHERE castsource = casttarget AND castfunc = 0; - oid | castsource | casttarget | castfunc | castcontext | castmethod ------+------------+------------+----------+-------------+------------ -(0 rows) - -SELECT c.* -FROM pg_cast c, pg_proc p -WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; - oid | castsource | casttarget | castfunc | castcontext | castmethod ------+------------+------------+----------+-------------+------------ -(0 rows) - --- Look for cast functions that don't have the right signature. The --- argument and result types in pg_proc must be the same as, or binary --- compatible with, what it says in pg_cast. --- As a special case, we allow casts from CHAR(n) that use functions --- declared to take TEXT. This does not pass the binary-coercibility test --- because CHAR(n)-to-TEXT normally invokes rtrim(). However, the results --- are the same, so long as the function is one that ignores trailing blanks. -SELECT c.* -FROM pg_cast c, pg_proc p -WHERE c.castfunc = p.oid AND - (p.pronargs < 1 OR p.pronargs > 3 - OR NOT (binary_coercible(c.castsource, p.proargtypes[0]) - OR (c.castsource = 'character'::regtype AND - p.proargtypes[0] = 'text'::regtype)) - OR NOT binary_coercible(p.prorettype, c.casttarget)); - oid | castsource | casttarget | castfunc | castcontext | castmethod ------+------------+------------+----------+-------------+------------ -(0 rows) - -SELECT c.* -FROM pg_cast c, pg_proc p -WHERE c.castfunc = p.oid AND - ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR - (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); - oid | castsource | casttarget | castfunc | castcontext | castmethod ------+------------+------------+----------+-------------+------------ -(0 rows) - --- Look for binary compatible casts that do not have the reverse --- direction registered as well, or where the reverse direction is not --- also binary compatible. This is legal, but usually not intended. --- As of 7.4, this finds the casts from text and varchar to bpchar, because --- those are binary-compatible while the reverse way goes through rtrim(). --- As of 8.2, this finds the cast from cidr to inet, because that is a --- trivial binary coercion while the other way goes through inet_to_cidr(). --- As of 8.3, this finds the casts from xml to text, varchar, and bpchar, --- because those are binary-compatible while the reverse goes through --- texttoxml(), which does an XML syntax check. --- As of 9.1, this finds the cast from pg_node_tree to text, which we --- intentionally do not provide a reverse pathway for. -SELECT castsource::regtype, casttarget::regtype, castfunc, castcontext -FROM pg_cast c -WHERE c.castmethod = 'b' AND - NOT EXISTS (SELECT 1 FROM pg_cast k - WHERE k.castmethod = 'b' AND - k.castsource = c.casttarget AND - k.casttarget = c.castsource); - castsource | casttarget | castfunc | castcontext --------------------+-------------------+----------+------------- - text | character | 0 | i - character varying | character | 0 | i - pg_node_tree | text | 0 | i - pg_ndistinct | bytea | 0 | i - pg_dependencies | bytea | 0 | i - pg_mcv_list | bytea | 0 | i - cidr | inet | 0 | i - xml | text | 0 | a - xml | character varying | 0 | a - xml | character | 0 | a -(10 rows) - --- **************** pg_conversion **************** --- Look for illegal values in pg_conversion fields. -SELECT c.oid, c.conname -FROM pg_conversion as c -WHERE c.conproc = 0 OR - pg_encoding_to_char(conforencoding) = '' OR - pg_encoding_to_char(contoencoding) = ''; - oid | conname ------+--------- -(0 rows) - --- Look for conprocs that don't have the expected signature. -SELECT p.oid, p.proname, c.oid, c.conname -FROM pg_proc p, pg_conversion c -WHERE p.oid = c.conproc AND - (p.prorettype != 'int4'::regtype OR p.proretset OR - p.pronargs != 6 OR - p.proargtypes[0] != 'int4'::regtype OR - p.proargtypes[1] != 'int4'::regtype OR - p.proargtypes[2] != 'cstring'::regtype OR - p.proargtypes[3] != 'internal'::regtype OR - p.proargtypes[4] != 'int4'::regtype OR - p.proargtypes[5] != 'bool'::regtype); - oid | proname | oid | conname ------+---------+-----+--------- -(0 rows) - --- Check for conprocs that don't perform the specific conversion that --- pg_conversion alleges they do, by trying to invoke each conversion --- on some simple ASCII data. (The conproc should throw an error if --- it doesn't accept the encodings that are passed to it.) --- Unfortunately, we can't test non-default conprocs this way, because --- there is no way to ask convert() to invoke them, and we cannot call --- them directly from SQL. But there are no non-default built-in --- conversions anyway. --- (Similarly, this doesn't cope with any search path issues.) -SELECT c.oid, c.conname -FROM pg_conversion as c -WHERE condefault AND - convert('ABC'::bytea, pg_encoding_to_char(conforencoding), - pg_encoding_to_char(contoencoding)) != 'ABC'; - oid | conname ------+--------- -(0 rows) - --- **************** pg_operator **************** --- Look for illegal values in pg_operator fields. -SELECT o1.oid, o1.oprname -FROM pg_operator as o1 -WHERE (o1.oprkind != 'b' AND o1.oprkind != 'l') OR - o1.oprresult = 0 OR o1.oprcode = 0; - oid | oprname ------+--------- -(0 rows) - --- Look for missing or unwanted operand types -SELECT o1.oid, o1.oprname -FROM pg_operator as o1 -WHERE (o1.oprleft = 0 and o1.oprkind != 'l') OR - (o1.oprleft != 0 and o1.oprkind = 'l') OR - o1.oprright = 0; - oid | oprname ------+--------- -(0 rows) - --- Look for conflicting operator definitions (same names and input datatypes). -SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode -FROM pg_operator AS o1, pg_operator AS o2 -WHERE o1.oid != o2.oid AND - o1.oprname = o2.oprname AND - o1.oprkind = o2.oprkind AND - o1.oprleft = o2.oprleft AND - o1.oprright = o2.oprright; - oid | oprcode | oid | oprcode ------+---------+-----+--------- -(0 rows) - --- Look for commutative operators that don't commute. --- DEFINITIONAL NOTE: If A.oprcom = B, then x A y has the same result as y B x. --- We expect that B will always say that B.oprcom = A as well; that's not --- inherently essential, but it would be inefficient not to mark it so. -SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode -FROM pg_operator AS o1, pg_operator AS o2 -WHERE o1.oprcom = o2.oid AND - (o1.oprkind != 'b' OR - o1.oprleft != o2.oprright OR - o1.oprright != o2.oprleft OR - o1.oprresult != o2.oprresult OR - o1.oid != o2.oprcom); - oid | oprcode | oid | oprcode ------+---------+-----+--------- -(0 rows) - --- Look for negatory operators that don't agree. --- DEFINITIONAL NOTE: If A.oprnegate = B, then both A and B must yield --- boolean results, and (x A y) == ! (x B y), or the equivalent for --- single-operand operators. --- We expect that B will always say that B.oprnegate = A as well; that's not --- inherently essential, but it would be inefficient not to mark it so. --- Also, A and B had better not be the same operator. -SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode -FROM pg_operator AS o1, pg_operator AS o2 -WHERE o1.oprnegate = o2.oid AND - (o1.oprkind != o2.oprkind OR - o1.oprleft != o2.oprleft OR - o1.oprright != o2.oprright OR - o1.oprresult != 'bool'::regtype OR - o2.oprresult != 'bool'::regtype OR - o1.oid != o2.oprnegate OR - o1.oid = o2.oid); - oid | oprcode | oid | oprcode ------+---------+-----+--------- -(0 rows) - --- Make a list of the names of operators that are claimed to be commutator --- pairs. This list will grow over time, but before accepting a new entry --- make sure you didn't link the wrong operators. -SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2 -FROM pg_operator o1, pg_operator o2 -WHERE o1.oprcom = o2.oid AND o1.oprname <= o2.oprname -ORDER BY 1, 2; - op1 | op2 -------+------ - # | # - & | & - && | && - * | * - *< | *> - *<= | *>= - *<> | *<> - *= | *= - + | + - -|- | -|- - < | > - <-> | <-> - << | >> - <<= | >>= - <= | >= - <> | <> - <@ | @> - = | = - ?# | ?# - ?- | ?- - ?-| | ?-| - ?| | ?| - ?|| | ?|| - @@ | @@ - @@@ | @@@ - | | | - ~<=~ | ~>=~ - ~<~ | ~>~ - ~= | ~= -(29 rows) - --- Likewise for negator pairs. -SELECT DISTINCT o1.oprname AS op1, o2.oprname AS op2 -FROM pg_operator o1, pg_operator o2 -WHERE o1.oprnegate = o2.oid AND o1.oprname <= o2.oprname -ORDER BY 1, 2; - op1 | op2 -------+------ - !~ | ~ - !~* | ~* - !~~ | ~~ - !~~* | ~~* - *< | *>= - *<= | *> - *<> | *= - < | >= - <= | > - <> | = - <> | ~= - ~<=~ | ~>~ - ~<~ | ~>=~ -(13 rows) - --- A mergejoinable or hashjoinable operator must be binary, must return --- boolean, and must have a commutator (itself, unless it's a cross-type --- operator). -SELECT o1.oid, o1.oprname FROM pg_operator AS o1 -WHERE (o1.oprcanmerge OR o1.oprcanhash) AND NOT - (o1.oprkind = 'b' AND o1.oprresult = 'bool'::regtype AND o1.oprcom != 0); - oid | oprname ------+--------- -(0 rows) - --- What's more, the commutator had better be mergejoinable/hashjoinable too. -SELECT o1.oid, o1.oprname, o2.oid, o2.oprname -FROM pg_operator AS o1, pg_operator AS o2 -WHERE o1.oprcom = o2.oid AND - (o1.oprcanmerge != o2.oprcanmerge OR - o1.oprcanhash != o2.oprcanhash); - oid | oprname | oid | oprname ------+---------+-----+--------- -(0 rows) - --- Mergejoinable operators should appear as equality members of btree index --- opfamilies. -SELECT o1.oid, o1.oprname -FROM pg_operator AS o1 -WHERE o1.oprcanmerge AND NOT EXISTS - (SELECT 1 FROM pg_amop - WHERE amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') AND - amopopr = o1.oid AND amopstrategy = 3); - oid | oprname ------+--------- -(0 rows) - --- And the converse. -SELECT o1.oid, o1.oprname, p.amopfamily -FROM pg_operator AS o1, pg_amop p -WHERE amopopr = o1.oid - AND amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') - AND amopstrategy = 3 - AND NOT o1.oprcanmerge; - oid | oprname | amopfamily ------+---------+------------ -(0 rows) - --- Hashable operators should appear as members of hash index opfamilies. -SELECT o1.oid, o1.oprname -FROM pg_operator AS o1 -WHERE o1.oprcanhash AND NOT EXISTS - (SELECT 1 FROM pg_amop - WHERE amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') AND - amopopr = o1.oid AND amopstrategy = 1); - oid | oprname ------+--------- -(0 rows) - --- And the converse. -SELECT o1.oid, o1.oprname, p.amopfamily -FROM pg_operator AS o1, pg_amop p -WHERE amopopr = o1.oid - AND amopmethod = (SELECT oid FROM pg_am WHERE amname = 'hash') - AND NOT o1.oprcanhash; - oid | oprname | amopfamily ------+---------+------------ -(0 rows) - --- Check that each operator defined in pg_operator matches its oprcode entry --- in pg_proc. Easiest to do this separately for each oprkind. -SELECT o1.oid, o1.oprname, p1.oid, p1.proname -FROM pg_operator AS o1, pg_proc AS p1 -WHERE o1.oprcode = p1.oid AND - o1.oprkind = 'b' AND - (p1.pronargs != 2 - OR NOT binary_coercible(p1.prorettype, o1.oprresult) - OR NOT binary_coercible(o1.oprleft, p1.proargtypes[0]) - OR NOT binary_coercible(o1.oprright, p1.proargtypes[1])); - oid | oprname | oid | proname ------+---------+-----+--------- -(0 rows) - -SELECT o1.oid, o1.oprname, p1.oid, p1.proname -FROM pg_operator AS o1, pg_proc AS p1 -WHERE o1.oprcode = p1.oid AND - o1.oprkind = 'l' AND - (p1.pronargs != 1 - OR NOT binary_coercible(p1.prorettype, o1.oprresult) - OR NOT binary_coercible(o1.oprright, p1.proargtypes[0]) - OR o1.oprleft != 0); - oid | oprname | oid | proname ------+---------+-----+--------- -(0 rows) - --- If the operator is mergejoinable or hashjoinable, its underlying function --- should not be volatile. -SELECT o1.oid, o1.oprname, p1.oid, p1.proname -FROM pg_operator AS o1, pg_proc AS p1 -WHERE o1.oprcode = p1.oid AND - (o1.oprcanmerge OR o1.oprcanhash) AND - p1.provolatile = 'v'; - oid | oprname | oid | proname ------+---------+-----+--------- -(0 rows) - --- If oprrest is set, the operator must return boolean, --- and it must link to a proc with the right signature --- to be a restriction selectivity estimator. --- The proc signature we want is: float8 proc(internal, oid, internal, int4) -SELECT o1.oid, o1.oprname, p2.oid, p2.proname -FROM pg_operator AS o1, pg_proc AS p2 -WHERE o1.oprrest = p2.oid AND - (o1.oprresult != 'bool'::regtype OR - p2.prorettype != 'float8'::regtype OR p2.proretset OR - p2.pronargs != 4 OR - p2.proargtypes[0] != 'internal'::regtype OR - p2.proargtypes[1] != 'oid'::regtype OR - p2.proargtypes[2] != 'internal'::regtype OR - p2.proargtypes[3] != 'int4'::regtype); - oid | oprname | oid | proname ------+---------+-----+--------- -(0 rows) - --- If oprjoin is set, the operator must be a binary boolean op, --- and it must link to a proc with the right signature --- to be a join selectivity estimator. --- The proc signature we want is: float8 proc(internal, oid, internal, int2, internal) --- (Note: the old signature with only 4 args is still allowed, but no core --- estimator should be using it.) -SELECT o1.oid, o1.oprname, p2.oid, p2.proname -FROM pg_operator AS o1, pg_proc AS p2 -WHERE o1.oprjoin = p2.oid AND - (o1.oprkind != 'b' OR o1.oprresult != 'bool'::regtype OR - p2.prorettype != 'float8'::regtype OR p2.proretset OR - p2.pronargs != 5 OR - p2.proargtypes[0] != 'internal'::regtype OR - p2.proargtypes[1] != 'oid'::regtype OR - p2.proargtypes[2] != 'internal'::regtype OR - p2.proargtypes[3] != 'int2'::regtype OR - p2.proargtypes[4] != 'internal'::regtype); - oid | oprname | oid | proname ------+---------+-----+--------- -(0 rows) - --- Insist that all built-in pg_operator entries have descriptions -SELECT o1.oid, o1.oprname -FROM pg_operator as o1 LEFT JOIN pg_description as d - ON o1.tableoid = d.classoid and o1.oid = d.objoid and d.objsubid = 0 -WHERE d.classoid IS NULL AND o1.oid <= 9999; - oid | oprname ------+--------- -(0 rows) - --- Check that operators' underlying functions have suitable comments, --- namely 'implementation of XXX operator'. (Note: it's not necessary to --- put such comments into pg_proc.dat; initdb will generate them as needed.) --- In some cases involving legacy names for operators, there are multiple --- operators referencing the same pg_proc entry, so ignore operators whose --- comments say they are deprecated. --- We also have a few functions that are both operator support and meant to --- be called directly; those should have comments matching their operator. -WITH funcdescs AS ( - SELECT p.oid as p_oid, proname, o.oid as o_oid, - pd.description as prodesc, - 'implementation of ' || oprname || ' operator' as expecteddesc, - od.description as oprdesc - FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid - LEFT JOIN pg_description pd ON - (pd.objoid = p.oid and pd.classoid = p.tableoid and pd.objsubid = 0) - LEFT JOIN pg_description od ON - (od.objoid = o.oid and od.classoid = o.tableoid and od.objsubid = 0) - WHERE o.oid <= 9999 -) -SELECT * FROM funcdescs - WHERE prodesc IS DISTINCT FROM expecteddesc - AND oprdesc NOT LIKE 'deprecated%' - AND prodesc IS DISTINCT FROM oprdesc; - p_oid | proname | o_oid | prodesc | expecteddesc | oprdesc --------+---------+-------+---------+--------------+--------- -(0 rows) - --- Show all the operator-implementation functions that have their own --- comments. This should happen only in cases where the function and --- operator syntaxes are both documented at the user level. --- This should be a pretty short list; it's mostly legacy cases. -WITH funcdescs AS ( - SELECT p.oid as p_oid, proname, o.oid as o_oid, - pd.description as prodesc, - 'implementation of ' || oprname || ' operator' as expecteddesc, - od.description as oprdesc - FROM pg_proc p JOIN pg_operator o ON oprcode = p.oid - LEFT JOIN pg_description pd ON - (pd.objoid = p.oid and pd.classoid = p.tableoid and pd.objsubid = 0) - LEFT JOIN pg_description od ON - (od.objoid = o.oid and od.classoid = o.tableoid and od.objsubid = 0) - WHERE o.oid <= 9999 -) -SELECT p_oid, proname, prodesc FROM funcdescs - WHERE prodesc IS DISTINCT FROM expecteddesc - AND oprdesc NOT LIKE 'deprecated%' -ORDER BY 1; - p_oid | proname | prodesc --------+-------------------------+------------------------------------------------- - 378 | array_append | append element onto end of array - 379 | array_prepend | prepend element onto front of array - 1035 | aclinsert | add/update ACL item - 1036 | aclremove | remove ACL item - 1037 | aclcontains | contains - 3217 | jsonb_extract_path | get value from jsonb with path elements - 3940 | jsonb_extract_path_text | get value from jsonb as text with path elements - 3951 | json_extract_path | get value from json with path elements - 3953 | json_extract_path_text | get value from json as text with path elements -(9 rows) - --- Operators that are commutator pairs should have identical volatility --- and leakproofness markings on their implementation functions. -SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode -FROM pg_operator AS o1, pg_operator AS o2, pg_proc AS p1, pg_proc AS p2 -WHERE o1.oprcom = o2.oid AND p1.oid = o1.oprcode AND p2.oid = o2.oprcode AND - (p1.provolatile != p2.provolatile OR - p1.proleakproof != p2.proleakproof); - oid | oprcode | oid | oprcode ------+---------+-----+--------- -(0 rows) - --- Likewise for negator pairs. -SELECT o1.oid, o1.oprcode, o2.oid, o2.oprcode -FROM pg_operator AS o1, pg_operator AS o2, pg_proc AS p1, pg_proc AS p2 -WHERE o1.oprnegate = o2.oid AND p1.oid = o1.oprcode AND p2.oid = o2.oprcode AND - (p1.provolatile != p2.provolatile OR - p1.proleakproof != p2.proleakproof); - oid | oprcode | oid | oprcode ------+---------+-----+--------- -(0 rows) - --- Btree comparison operators' functions should have the same volatility --- and leakproofness markings as the associated comparison support function. -SELECT pp.oid::regprocedure as proc, pp.provolatile as vp, pp.proleakproof as lp, - po.oid::regprocedure as opr, po.provolatile as vo, po.proleakproof as lo -FROM pg_proc pp, pg_proc po, pg_operator o, pg_amproc ap, pg_amop ao -WHERE pp.oid = ap.amproc AND po.oid = o.oprcode AND o.oid = ao.amopopr AND - ao.amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') AND - ao.amopfamily = ap.amprocfamily AND - ao.amoplefttype = ap.amproclefttype AND - ao.amoprighttype = ap.amprocrighttype AND - ap.amprocnum = 1 AND - (pp.provolatile != po.provolatile OR - pp.proleakproof != po.proleakproof) -ORDER BY 1; - proc | vp | lp | opr | vo | lo -------+----+----+-----+----+---- -(0 rows) - --- **************** pg_aggregate **************** --- Look for illegal values in pg_aggregate fields. -SELECT ctid, aggfnoid::oid -FROM pg_aggregate as a -WHERE aggfnoid = 0 OR aggtransfn = 0 OR - aggkind NOT IN ('n', 'o', 'h') OR - aggnumdirectargs < 0 OR - (aggkind = 'n' AND aggnumdirectargs > 0) OR - aggfinalmodify NOT IN ('r', 's', 'w') OR - aggmfinalmodify NOT IN ('r', 's', 'w') OR - aggtranstype = 0 OR aggmtransspace < 0; - ctid | aggfnoid -------+---------- -(0 rows) - --- Make sure the matching pg_proc entry is sensible, too. -SELECT a.aggfnoid::oid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggfnoid = p.oid AND - (p.prokind != 'a' OR p.proretset OR p.pronargs < a.aggnumdirectargs); - aggfnoid | proname -----------+--------- -(0 rows) - --- Make sure there are no prokind = PROKIND_AGGREGATE pg_proc entries without matches. -SELECT oid, proname -FROM pg_proc as p -WHERE p.prokind = 'a' AND - NOT EXISTS (SELECT 1 FROM pg_aggregate a WHERE a.aggfnoid = p.oid); - oid | proname ------+--------- -(0 rows) - --- If there is no finalfn then the output type must be the transtype. -SELECT a.aggfnoid::oid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggfnoid = p.oid AND - a.aggfinalfn = 0 AND p.prorettype != a.aggtranstype; - aggfnoid | proname -----------+--------- -(0 rows) - --- Cross-check transfn against its entry in pg_proc. -SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr -WHERE a.aggfnoid = p.oid AND - a.aggtransfn = ptr.oid AND - (ptr.proretset - OR NOT (ptr.pronargs = - CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 - ELSE greatest(p.pronargs - a.aggnumdirectargs, 1) + 1 END) - OR NOT binary_coercible(ptr.prorettype, a.aggtranstype) - OR NOT binary_coercible(a.aggtranstype, ptr.proargtypes[0]) - OR (p.pronargs > 0 AND - NOT binary_coercible(p.proargtypes[0], ptr.proargtypes[1])) - OR (p.pronargs > 1 AND - NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2])) - OR (p.pronargs > 2 AND - NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3])) - OR (p.pronargs > 3 AND - NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4])) - -- we could carry the check further, but 4 args is enough for now - OR (p.pronargs > 4) - ); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- Cross-check finalfn (if present) against its entry in pg_proc. -SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn -WHERE a.aggfnoid = p.oid AND - a.aggfinalfn = pfn.oid AND - (pfn.proretset OR - NOT binary_coercible(pfn.prorettype, p.prorettype) OR - NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]) OR - CASE WHEN a.aggfinalextra THEN pfn.pronargs != p.pronargs + 1 - ELSE pfn.pronargs != a.aggnumdirectargs + 1 END - OR (pfn.pronargs > 1 AND - NOT binary_coercible(p.proargtypes[0], pfn.proargtypes[1])) - OR (pfn.pronargs > 2 AND - NOT binary_coercible(p.proargtypes[1], pfn.proargtypes[2])) - OR (pfn.pronargs > 3 AND - NOT binary_coercible(p.proargtypes[2], pfn.proargtypes[3])) - -- we could carry the check further, but 4 args is enough for now - OR (pfn.pronargs > 4) - ); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- If transfn is strict then either initval should be non-NULL, or --- input type should match transtype so that the first non-null input --- can be assigned as the state value. -SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr -WHERE a.aggfnoid = p.oid AND - a.aggtransfn = ptr.oid AND ptr.proisstrict AND - a.agginitval IS NULL AND - NOT binary_coercible(p.proargtypes[0], a.aggtranstype); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- Check for inconsistent specifications of moving-aggregate columns. -SELECT ctid, aggfnoid::oid -FROM pg_aggregate as a -WHERE aggmtranstype != 0 AND - (aggmtransfn = 0 OR aggminvtransfn = 0); - ctid | aggfnoid -------+---------- -(0 rows) - -SELECT ctid, aggfnoid::oid -FROM pg_aggregate as a -WHERE aggmtranstype = 0 AND - (aggmtransfn != 0 OR aggminvtransfn != 0 OR aggmfinalfn != 0 OR - aggmtransspace != 0 OR aggminitval IS NOT NULL); - ctid | aggfnoid -------+---------- -(0 rows) - --- If there is no mfinalfn then the output type must be the mtranstype. -SELECT a.aggfnoid::oid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggfnoid = p.oid AND - a.aggmtransfn != 0 AND - a.aggmfinalfn = 0 AND p.prorettype != a.aggmtranstype; - aggfnoid | proname -----------+--------- -(0 rows) - --- Cross-check mtransfn (if present) against its entry in pg_proc. -SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr -WHERE a.aggfnoid = p.oid AND - a.aggmtransfn = ptr.oid AND - (ptr.proretset - OR NOT (ptr.pronargs = - CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 - ELSE greatest(p.pronargs - a.aggnumdirectargs, 1) + 1 END) - OR NOT binary_coercible(ptr.prorettype, a.aggmtranstype) - OR NOT binary_coercible(a.aggmtranstype, ptr.proargtypes[0]) - OR (p.pronargs > 0 AND - NOT binary_coercible(p.proargtypes[0], ptr.proargtypes[1])) - OR (p.pronargs > 1 AND - NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2])) - OR (p.pronargs > 2 AND - NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3])) - -- we could carry the check further, but 3 args is enough for now - OR (p.pronargs > 3) - ); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- Cross-check minvtransfn (if present) against its entry in pg_proc. -SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr -WHERE a.aggfnoid = p.oid AND - a.aggminvtransfn = ptr.oid AND - (ptr.proretset - OR NOT (ptr.pronargs = - CASE WHEN a.aggkind = 'n' THEN p.pronargs + 1 - ELSE greatest(p.pronargs - a.aggnumdirectargs, 1) + 1 END) - OR NOT binary_coercible(ptr.prorettype, a.aggmtranstype) - OR NOT binary_coercible(a.aggmtranstype, ptr.proargtypes[0]) - OR (p.pronargs > 0 AND - NOT binary_coercible(p.proargtypes[0], ptr.proargtypes[1])) - OR (p.pronargs > 1 AND - NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2])) - OR (p.pronargs > 2 AND - NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3])) - -- we could carry the check further, but 3 args is enough for now - OR (p.pronargs > 3) - ); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- Cross-check mfinalfn (if present) against its entry in pg_proc. -SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn -WHERE a.aggfnoid = p.oid AND - a.aggmfinalfn = pfn.oid AND - (pfn.proretset OR - NOT binary_coercible(pfn.prorettype, p.prorettype) OR - NOT binary_coercible(a.aggmtranstype, pfn.proargtypes[0]) OR - CASE WHEN a.aggmfinalextra THEN pfn.pronargs != p.pronargs + 1 - ELSE pfn.pronargs != a.aggnumdirectargs + 1 END - OR (pfn.pronargs > 1 AND - NOT binary_coercible(p.proargtypes[0], pfn.proargtypes[1])) - OR (pfn.pronargs > 2 AND - NOT binary_coercible(p.proargtypes[1], pfn.proargtypes[2])) - OR (pfn.pronargs > 3 AND - NOT binary_coercible(p.proargtypes[2], pfn.proargtypes[3])) - -- we could carry the check further, but 4 args is enough for now - OR (pfn.pronargs > 4) - ); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- If mtransfn is strict then either minitval should be non-NULL, or --- input type should match mtranstype so that the first non-null input --- can be assigned as the state value. -SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr -WHERE a.aggfnoid = p.oid AND - a.aggmtransfn = ptr.oid AND ptr.proisstrict AND - a.aggminitval IS NULL AND - NOT binary_coercible(p.proargtypes[0], a.aggmtranstype); - aggfnoid | proname | oid | proname -----------+---------+-----+--------- -(0 rows) - --- mtransfn and minvtransfn should have same strictness setting. -SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname, iptr.oid, iptr.proname -FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr, pg_proc AS iptr -WHERE a.aggfnoid = p.oid AND - a.aggmtransfn = ptr.oid AND - a.aggminvtransfn = iptr.oid AND - ptr.proisstrict != iptr.proisstrict; - aggfnoid | proname | oid | proname | oid | proname -----------+---------+-----+---------+-----+--------- -(0 rows) - --- Check that all combine functions have signature --- combine(transtype, transtype) returns transtype -SELECT a.aggfnoid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggcombinefn = p.oid AND - (p.pronargs != 2 OR - p.prorettype != p.proargtypes[0] OR - p.prorettype != p.proargtypes[1] OR - NOT binary_coercible(a.aggtranstype, p.proargtypes[0])); - aggfnoid | proname -----------+--------- -(0 rows) - --- Check that no combine function for an INTERNAL transtype is strict. -SELECT a.aggfnoid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggcombinefn = p.oid AND - a.aggtranstype = 'internal'::regtype AND p.proisstrict; - aggfnoid | proname -----------+--------- -(0 rows) - --- serialize/deserialize functions should be specified only for aggregates --- with transtype internal and a combine function, and we should have both --- or neither of them. -SELECT aggfnoid, aggtranstype, aggserialfn, aggdeserialfn -FROM pg_aggregate -WHERE (aggserialfn != 0 OR aggdeserialfn != 0) - AND (aggtranstype != 'internal'::regtype OR aggcombinefn = 0 OR - aggserialfn = 0 OR aggdeserialfn = 0); - aggfnoid | aggtranstype | aggserialfn | aggdeserialfn -----------+--------------+-------------+--------------- -(0 rows) - --- Check that all serialization functions have signature --- serialize(internal) returns bytea --- Also insist that they be strict; it's wasteful to run them on NULLs. -SELECT a.aggfnoid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggserialfn = p.oid AND - (p.prorettype != 'bytea'::regtype OR p.pronargs != 1 OR - p.proargtypes[0] != 'internal'::regtype OR - NOT p.proisstrict); - aggfnoid | proname -----------+--------- -(0 rows) - --- Check that all deserialization functions have signature --- deserialize(bytea, internal) returns internal --- Also insist that they be strict; it's wasteful to run them on NULLs. -SELECT a.aggfnoid, p.proname -FROM pg_aggregate as a, pg_proc as p -WHERE a.aggdeserialfn = p.oid AND - (p.prorettype != 'internal'::regtype OR p.pronargs != 2 OR - p.proargtypes[0] != 'bytea'::regtype OR - p.proargtypes[1] != 'internal'::regtype OR - NOT p.proisstrict); - aggfnoid | proname -----------+--------- -(0 rows) - --- Check that aggregates which have the same transition function also have --- the same combine, serialization, and deserialization functions. --- While that isn't strictly necessary, it's fishy if they don't. -SELECT a.aggfnoid, a.aggcombinefn, a.aggserialfn, a.aggdeserialfn, - b.aggfnoid, b.aggcombinefn, b.aggserialfn, b.aggdeserialfn -FROM - pg_aggregate a, pg_aggregate b -WHERE - a.aggfnoid < b.aggfnoid AND a.aggtransfn = b.aggtransfn AND - (a.aggcombinefn != b.aggcombinefn OR a.aggserialfn != b.aggserialfn - OR a.aggdeserialfn != b.aggdeserialfn); - aggfnoid | aggcombinefn | aggserialfn | aggdeserialfn | aggfnoid | aggcombinefn | aggserialfn | aggdeserialfn -----------+--------------+-------------+---------------+----------+--------------+-------------+--------------- -(0 rows) - --- Cross-check aggsortop (if present) against pg_operator. --- We expect to find entries for bool_and, bool_or, every, max, and min. -SELECT DISTINCT proname, oprname -FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p -WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid -ORDER BY 1, 2; - proname | oprname -----------+--------- - bool_and | < - bool_or | > - every | < - max | > - min | < -(5 rows) - --- Check datatypes match -SELECT a.aggfnoid::oid, o.oid -FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p -WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND - (oprkind != 'b' OR oprresult != 'boolean'::regtype - OR oprleft != p.proargtypes[0] OR oprright != p.proargtypes[0]); - aggfnoid | oid -----------+----- -(0 rows) - --- Check operator is a suitable btree opfamily member -SELECT a.aggfnoid::oid, o.oid -FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p -WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND - NOT EXISTS(SELECT 1 FROM pg_amop - WHERE amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') - AND amopopr = o.oid - AND amoplefttype = o.oprleft - AND amoprighttype = o.oprright); - aggfnoid | oid -----------+----- -(0 rows) - --- Check correspondence of btree strategies and names -SELECT DISTINCT proname, oprname, amopstrategy -FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p, - pg_amop as ao -WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND - amopopr = o.oid AND - amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') -ORDER BY 1, 2; - proname | oprname | amopstrategy -----------+---------+-------------- - bool_and | < | 1 - bool_or | > | 5 - every | < | 1 - max | > | 5 - min | < | 1 -(5 rows) - --- Check that there are not aggregates with the same name and different --- numbers of arguments. While not technically wrong, we have a project policy --- to avoid this because it opens the door for confusion in connection with --- ORDER BY: novices frequently put the ORDER BY in the wrong place. --- See the fate of the single-argument form of string_agg() for history. --- (Note: we don't forbid users from creating such aggregates; the policy is --- just to think twice before creating built-in aggregates like this.) --- The only aggregates that should show up here are count(x) and count(*). -SELECT p1.oid::regprocedure, p2.oid::regprocedure -FROM pg_proc AS p1, pg_proc AS p2 -WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND - p1.prokind = 'a' AND p2.prokind = 'a' AND - array_dims(p1.proargtypes) != array_dims(p2.proargtypes) -ORDER BY 1; - oid | oid ---------------+--------- - count("any") | count() -(1 row) - --- For the same reason, built-in aggregates with default arguments are no good. -SELECT oid, proname -FROM pg_proc AS p -WHERE prokind = 'a' AND proargdefaults IS NOT NULL; - oid | proname ------+--------- -(0 rows) - --- For the same reason, we avoid creating built-in variadic aggregates, except --- that variadic ordered-set aggregates are OK (since they have special syntax --- that is not subject to the misplaced ORDER BY issue). -SELECT p.oid, proname -FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid -WHERE prokind = 'a' AND provariadic != 0 AND a.aggkind = 'n'; - oid | proname ------+--------- -(0 rows) - --- **************** pg_opfamily **************** --- Look for illegal values in pg_opfamily fields -SELECT f.oid -FROM pg_opfamily as f -WHERE f.opfmethod = 0 OR f.opfnamespace = 0; - oid ------ -(0 rows) - --- Look for opfamilies having no opclasses. While most validation of --- opfamilies is now handled by AM-specific amvalidate functions, that's --- driven from pg_opclass entries below, so an empty opfamily would not --- get noticed. -SELECT oid, opfname FROM pg_opfamily f -WHERE NOT EXISTS (SELECT 1 FROM pg_opclass WHERE opcfamily = f.oid); - oid | opfname ------+--------- -(0 rows) - --- **************** pg_opclass **************** --- Look for illegal values in pg_opclass fields -SELECT c1.oid -FROM pg_opclass AS c1 -WHERE c1.opcmethod = 0 OR c1.opcnamespace = 0 OR c1.opcfamily = 0 - OR c1.opcintype = 0; - oid ------ -(0 rows) - --- opcmethod must match owning opfamily's opfmethod -SELECT c1.oid, f1.oid -FROM pg_opclass AS c1, pg_opfamily AS f1 -WHERE c1.opcfamily = f1.oid AND c1.opcmethod != f1.opfmethod; - oid | oid ------+----- -(0 rows) - --- There should not be multiple entries in pg_opclass with opcdefault true --- and the same opcmethod/opcintype combination. -SELECT c1.oid, c2.oid -FROM pg_opclass AS c1, pg_opclass AS c2 -WHERE c1.oid != c2.oid AND - c1.opcmethod = c2.opcmethod AND c1.opcintype = c2.opcintype AND - c1.opcdefault AND c2.opcdefault; - oid | oid ------+----- -(0 rows) - --- Ask access methods to validate opclasses --- (this replaces a lot of SQL-level checks that used to be done in this file) -SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate(oid); - oid | opcname ------+--------- -(0 rows) - --- **************** pg_am **************** --- Look for illegal values in pg_am fields -SELECT a1.oid, a1.amname -FROM pg_am AS a1 -WHERE a1.amhandler = 0; - oid | amname ------+-------- -(0 rows) - --- Check for index amhandler functions with the wrong signature -SELECT a1.oid, a1.amname, p1.oid, p1.proname -FROM pg_am AS a1, pg_proc AS p1 -WHERE p1.oid = a1.amhandler AND a1.amtype = 'i' AND - (p1.prorettype != 'index_am_handler'::regtype - OR p1.proretset - OR p1.pronargs != 1 - OR p1.proargtypes[0] != 'internal'::regtype); - oid | amname | oid | proname ------+--------+-----+--------- -(0 rows) - --- Check for table amhandler functions with the wrong signature -SELECT a1.oid, a1.amname, p1.oid, p1.proname -FROM pg_am AS a1, pg_proc AS p1 -WHERE p1.oid = a1.amhandler AND a1.amtype = 't' AND - (p1.prorettype != 'table_am_handler'::regtype - OR p1.proretset - OR p1.pronargs != 1 - OR p1.proargtypes[0] != 'internal'::regtype); - oid | amname | oid | proname ------+--------+-----+--------- -(0 rows) - --- **************** pg_amop **************** --- Look for illegal values in pg_amop fields -SELECT a1.amopfamily, a1.amopstrategy -FROM pg_amop as a1 -WHERE a1.amopfamily = 0 OR a1.amoplefttype = 0 OR a1.amoprighttype = 0 - OR a1.amopopr = 0 OR a1.amopmethod = 0 OR a1.amopstrategy < 1; - amopfamily | amopstrategy -------------+-------------- -(0 rows) - -SELECT a1.amopfamily, a1.amopstrategy -FROM pg_amop as a1 -WHERE NOT ((a1.amoppurpose = 's' AND a1.amopsortfamily = 0) OR - (a1.amoppurpose = 'o' AND a1.amopsortfamily <> 0)); - amopfamily | amopstrategy -------------+-------------- -(0 rows) - --- amopmethod must match owning opfamily's opfmethod -SELECT a1.oid, f1.oid -FROM pg_amop AS a1, pg_opfamily AS f1 -WHERE a1.amopfamily = f1.oid AND a1.amopmethod != f1.opfmethod; - oid | oid ------+----- -(0 rows) - --- Make a list of all the distinct operator names being used in particular --- strategy slots. This is a bit hokey, since the list might need to change --- in future releases, but it's an effective way of spotting mistakes such as --- swapping two operators within a family. -SELECT DISTINCT amopmethod, amopstrategy, oprname -FROM pg_amop a1 LEFT JOIN pg_operator o1 ON amopopr = o1.oid -ORDER BY 1, 2, 3; - amopmethod | amopstrategy | oprname -------------+--------------+--------- - 403 | 1 | *< - 403 | 1 | < - 403 | 1 | ~<~ - 403 | 2 | *<= - 403 | 2 | <= - 403 | 2 | ~<=~ - 403 | 3 | *= - 403 | 3 | = - 403 | 4 | *>= - 403 | 4 | >= - 403 | 4 | ~>=~ - 403 | 5 | *> - 403 | 5 | > - 403 | 5 | ~>~ - 405 | 1 | = - 783 | 1 | << - 783 | 1 | @@ - 783 | 2 | &< - 783 | 3 | && - 783 | 4 | &> - 783 | 5 | >> - 783 | 6 | -|- - 783 | 6 | ~= - 783 | 7 | @> - 783 | 8 | <@ - 783 | 9 | &<| - 783 | 10 | <<| - 783 | 11 | |>> - 783 | 12 | |&> - 783 | 15 | <-> - 783 | 16 | @> - 783 | 18 | = - 783 | 19 | <> - 783 | 20 | < - 783 | 21 | <= - 783 | 22 | > - 783 | 23 | >= - 783 | 24 | << - 783 | 25 | <<= - 783 | 26 | >> - 783 | 27 | >>= - 783 | 28 | <@ - 783 | 29 | <^ - 783 | 30 | >^ - 783 | 48 | <@ - 783 | 68 | <@ - 2742 | 1 | && - 2742 | 1 | @@ - 2742 | 2 | @> - 2742 | 2 | @@@ - 2742 | 3 | <@ - 2742 | 4 | = - 2742 | 7 | @> - 2742 | 9 | ? - 2742 | 10 | ?| - 2742 | 11 | ?& - 2742 | 15 | @? - 2742 | 16 | @@ - 3580 | 1 | < - 3580 | 1 | << - 3580 | 1 | = - 3580 | 2 | &< - 3580 | 2 | <= - 3580 | 3 | && - 3580 | 3 | = - 3580 | 4 | &> - 3580 | 4 | >= - 3580 | 5 | > - 3580 | 5 | >> - 3580 | 6 | ~= - 3580 | 7 | >>= - 3580 | 7 | @> - 3580 | 8 | <<= - 3580 | 8 | <@ - 3580 | 9 | &<| - 3580 | 10 | <<| - 3580 | 11 | |>> - 3580 | 12 | |&> - 3580 | 16 | @> - 3580 | 17 | -|- - 3580 | 18 | = - 3580 | 20 | < - 3580 | 21 | <= - 3580 | 22 | > - 3580 | 23 | >= - 3580 | 24 | >> - 3580 | 26 | << - 4000 | 1 | << - 4000 | 1 | ~<~ - 4000 | 2 | &< - 4000 | 2 | ~<=~ - 4000 | 3 | && - 4000 | 3 | = - 4000 | 4 | &> - 4000 | 4 | ~>=~ - 4000 | 5 | >> - 4000 | 5 | ~>~ - 4000 | 6 | -|- - 4000 | 6 | ~= - 4000 | 7 | @> - 4000 | 8 | <@ - 4000 | 9 | &<| - 4000 | 10 | <<| - 4000 | 11 | < - 4000 | 11 | |>> - 4000 | 12 | <= - 4000 | 12 | |&> - 4000 | 14 | >= - 4000 | 15 | <-> - 4000 | 15 | > - 4000 | 16 | @> - 4000 | 18 | = - 4000 | 19 | <> - 4000 | 20 | < - 4000 | 21 | <= - 4000 | 22 | > - 4000 | 23 | >= - 4000 | 24 | << - 4000 | 25 | <<= - 4000 | 26 | >> - 4000 | 27 | >>= - 4000 | 28 | ^@ - 4000 | 29 | <^ - 4000 | 30 | >^ -(124 rows) - --- Check that all opclass search operators have selectivity estimators. --- This is not absolutely required, but it seems a reasonable thing --- to insist on for all standard datatypes. -SELECT a1.amopfamily, a1.amopopr, o1.oid, o1.oprname -FROM pg_amop AS a1, pg_operator AS o1 -WHERE a1.amopopr = o1.oid AND a1.amoppurpose = 's' AND - (o1.oprrest = 0 OR o1.oprjoin = 0); - amopfamily | amopopr | oid | oprname -------------+---------+-----+--------- -(0 rows) - --- Check that each opclass in an opfamily has associated operators, that is --- ones whose oprleft matches opcintype (possibly by coercion). -SELECT c1.opcname, c1.opcfamily -FROM pg_opclass AS c1 -WHERE NOT EXISTS(SELECT 1 FROM pg_amop AS a1 - WHERE a1.amopfamily = c1.opcfamily - AND binary_coercible(c1.opcintype, a1.amoplefttype)); - opcname | opcfamily ----------+----------- -(0 rows) - --- Check that each operator listed in pg_amop has an associated opclass, --- that is one whose opcintype matches oprleft (possibly by coercion). --- Otherwise the operator is useless because it cannot be matched to an index. --- (In principle it could be useful to list such operators in multiple-datatype --- btree opfamilies, but in practice you'd expect there to be an opclass for --- every datatype the family knows about.) -SELECT a1.amopfamily, a1.amopstrategy, a1.amopopr -FROM pg_amop AS a1 -WHERE NOT EXISTS(SELECT 1 FROM pg_opclass AS c1 - WHERE c1.opcfamily = a1.amopfamily - AND binary_coercible(c1.opcintype, a1.amoplefttype)); - amopfamily | amopstrategy | amopopr -------------+--------------+--------- -(0 rows) - --- Operators that are primary members of opclasses must be immutable (else --- it suggests that the index ordering isn't fixed). Operators that are --- cross-type members need only be stable, since they are just shorthands --- for index probe queries. -SELECT a1.amopfamily, a1.amopopr, o1.oprname, p1.prosrc -FROM pg_amop AS a1, pg_operator AS o1, pg_proc AS p1 -WHERE a1.amopopr = o1.oid AND o1.oprcode = p1.oid AND - a1.amoplefttype = a1.amoprighttype AND - p1.provolatile != 'i'; - amopfamily | amopopr | oprname | prosrc -------------+---------+---------+-------- -(0 rows) - -SELECT a1.amopfamily, a1.amopopr, o1.oprname, p1.prosrc -FROM pg_amop AS a1, pg_operator AS o1, pg_proc AS p1 -WHERE a1.amopopr = o1.oid AND o1.oprcode = p1.oid AND - a1.amoplefttype != a1.amoprighttype AND - p1.provolatile = 'v'; - amopfamily | amopopr | oprname | prosrc -------------+---------+---------+-------- -(0 rows) - --- **************** pg_amproc **************** --- Look for illegal values in pg_amproc fields -SELECT a1.amprocfamily, a1.amprocnum -FROM pg_amproc as a1 -WHERE a1.amprocfamily = 0 OR a1.amproclefttype = 0 OR a1.amprocrighttype = 0 - OR a1.amprocnum < 0 OR a1.amproc = 0; - amprocfamily | amprocnum ---------------+----------- -(0 rows) - --- Support routines that are primary members of opfamilies must be immutable --- (else it suggests that the index ordering isn't fixed). But cross-type --- members need only be stable, since they are just shorthands --- for index probe queries. -SELECT a1.amprocfamily, a1.amproc, p1.prosrc -FROM pg_amproc AS a1, pg_proc AS p1 -WHERE a1.amproc = p1.oid AND - a1.amproclefttype = a1.amprocrighttype AND - p1.provolatile != 'i'; - amprocfamily | amproc | prosrc ---------------+--------+-------- -(0 rows) - -SELECT a1.amprocfamily, a1.amproc, p1.prosrc -FROM pg_amproc AS a1, pg_proc AS p1 -WHERE a1.amproc = p1.oid AND - a1.amproclefttype != a1.amprocrighttype AND - p1.provolatile = 'v'; - amprocfamily | amproc | prosrc ---------------+--------+-------- -(0 rows) - --- Almost all of the core distribution's Btree opclasses can use one of the --- two generic "equalimage" functions as their support function 4. Look for --- opclasses that don't allow deduplication unconditionally here. --- --- Newly added Btree opclasses don't have to support deduplication. It will --- usually be trivial to add support, though. Note that the expected output --- of this part of the test will need to be updated when a new opclass cannot --- support deduplication (by using btequalimage). -SELECT amp.amproc::regproc AS proc, opf.opfname AS opfamily_name, - opc.opcname AS opclass_name, opc.opcintype::regtype AS opcintype -FROM pg_am AS am -JOIN pg_opclass AS opc ON opc.opcmethod = am.oid -JOIN pg_opfamily AS opf ON opc.opcfamily = opf.oid -LEFT JOIN pg_amproc AS amp ON amp.amprocfamily = opf.oid AND - amp.amproclefttype = opc.opcintype AND amp.amprocnum = 4 -WHERE am.amname = 'btree' AND - amp.amproc IS DISTINCT FROM 'btequalimage'::regproc -ORDER BY 1, 2, 3; - proc | opfamily_name | opclass_name | opcintype ---------------------+------------------+------------------+------------------ - btvarstrequalimage | bpchar_ops | bpchar_ops | character - btvarstrequalimage | text_ops | name_ops | name - btvarstrequalimage | text_ops | text_ops | text - btvarstrequalimage | text_ops | varchar_ops | text - | array_ops | array_ops | anyarray - | float_ops | float4_ops | real - | float_ops | float8_ops | double precision - | interval_ops | interval_ops | interval - | jsonb_ops | jsonb_ops | jsonb - | multirange_ops | multirange_ops | anymultirange - | numeric_ops | numeric_ops | numeric - | range_ops | range_ops | anyrange - | record_image_ops | record_image_ops | record - | record_ops | record_ops | record - | tsquery_ops | tsquery_ops | tsquery - | tsvector_ops | tsvector_ops | tsvector -(16 rows) - --- **************** pg_index **************** --- Look for illegal values in pg_index fields. -SELECT indexrelid, indrelid -FROM pg_index -WHERE indexrelid = 0 OR indrelid = 0 OR - indnatts <= 0 OR indnatts > 32; - indexrelid | indrelid -------------+---------- -(0 rows) - --- oidvector and int2vector fields should be of length indnatts. -SELECT indexrelid, indrelid -FROM pg_index -WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR - array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR - array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR - array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1; - indexrelid | indrelid -------------+---------- -(0 rows) - --- Check that opclasses and collations match the underlying columns. --- (As written, this test ignores expression indexes.) -SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname -FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey, - unnest(indclass) as iclass, unnest(indcollation) as icoll - FROM pg_index) ss, - pg_attribute a, - pg_opclass opc -WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND - (NOT binary_coercible(atttypid, opcintype) OR icoll != attcollation); - indexrelid | indrelid | attname | atttypid | opcname -------------+----------+---------+----------+--------- -(0 rows) - --- For system catalogs, be even tighter: nearly all indexes should be --- exact type matches not binary-coercible matches. At this writing --- the only exception is an OID index on a regproc column. -SELECT indexrelid::regclass, indrelid::regclass, attname, atttypid::regtype, opcname -FROM (SELECT indexrelid, indrelid, unnest(indkey) as ikey, - unnest(indclass) as iclass, unnest(indcollation) as icoll - FROM pg_index - WHERE indrelid < 16384) ss, - pg_attribute a, - pg_opclass opc -WHERE a.attrelid = indrelid AND a.attnum = ikey AND opc.oid = iclass AND - (opcintype != atttypid OR icoll != attcollation) -ORDER BY 1; - indexrelid | indrelid | attname | atttypid | opcname ---------------------------+--------------+----------+----------+--------- - pg_aggregate_fnoid_index | pg_aggregate | aggfnoid | regproc | oid_ops -(1 row) - --- Check for system catalogs with collation-sensitive ordering. This is not --- a representational error in pg_index, but simply wrong catalog design. --- It's bad because we expect to be able to clone template0 and assign the --- copy a different database collation. It would especially not work for --- shared catalogs. -SELECT relname, attname, attcollation -FROM pg_class c, pg_attribute a -WHERE c.oid = attrelid AND c.oid < 16384 AND - c.relkind != 'v' AND -- we don't care about columns in views - attcollation != 0 AND - attcollation != (SELECT oid FROM pg_collation WHERE collname = 'C'); - relname | attname | attcollation ----------+---------+-------------- -(0 rows) - --- Double-check that collation-sensitive indexes have "C" collation, too. -SELECT indexrelid::regclass, indrelid::regclass, iclass, icoll -FROM (SELECT indexrelid, indrelid, - unnest(indclass) as iclass, unnest(indcollation) as icoll - FROM pg_index - WHERE indrelid < 16384) ss -WHERE icoll != 0 AND - icoll != (SELECT oid FROM pg_collation WHERE collname = 'C'); - indexrelid | indrelid | iclass | icoll -------------+----------+--------+------- -(0 rows) - +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/join.out /tmp/cirrus-ci-build/src/test/regress/results/join.out --- /tmp/cirrus-ci-build/src/test/regress/expected/join.out 2026-03-09 20:33:45.168311045 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/join.out 2026-03-09 20:39:09.948088740 +0000 @@ -2892,7196 +2892,8 @@ (3 rows) DELETE FROM t3 USING t1 JOIN t2 USING (a) WHERE t3.x > t1.a; -SELECT * FROM t3; - x | y ----+--- - 6 | 7 - 7 | 8 -(2 rows) - -DELETE FROM t3 USING t3 t3_other WHERE t3.x = t3_other.x AND t3.y = t3_other.y; -SELECT * FROM t3; - x | y ----+--- -(0 rows) - --- Test join against inheritance tree -create temp table t2a () inherits (t2); -insert into t2a values (200, 2001); -select * from t1 left join t2 on (t1.a = t2.a); - a | b | a | b ------+------+-----+------ - 5 | 10 | | - 15 | 20 | | - 100 | 100 | | - 200 | 1000 | 200 | 2000 - 200 | 1000 | 200 | 2001 -(5 rows) - --- Test matching of column name with wrong alias -select t1.x from t1 join t3 on (t1.a = t3.x); -ERROR: column t1.x does not exist -LINE 1: select t1.x from t1 join t3 on (t1.a = t3.x); - ^ -HINT: Perhaps you meant to reference the column "t3.x". --- Test matching of locking clause with wrong alias -select t1.*, t2.*, unnamed_join.* from - t1 join t2 on (t1.a = t2.a), t3 as unnamed_join - for update of unnamed_join; - a | b | a | b | x | y ----+---+---+---+---+--- -(0 rows) - -select foo.*, unnamed_join.* from - t1 join t2 using (a) as foo, t3 as unnamed_join - for update of unnamed_join; - a | x | y ----+---+--- -(0 rows) - -select foo.*, unnamed_join.* from - t1 join t2 using (a) as foo, t3 as unnamed_join - for update of foo; -ERROR: FOR UPDATE cannot be applied to a join -LINE 3: for update of foo; - ^ -select bar.*, unnamed_join.* from - (t1 join t2 using (a) as foo) as bar, t3 as unnamed_join - for update of foo; -ERROR: relation "foo" in FOR UPDATE clause not found in FROM clause -LINE 3: for update of foo; - ^ -select bar.*, unnamed_join.* from - (t1 join t2 using (a) as foo) as bar, t3 as unnamed_join - for update of bar; -ERROR: FOR UPDATE cannot be applied to a join -LINE 3: for update of bar; - ^ --- --- regression test for 8.1 merge right join bug --- -CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 ); -INSERT INTO tt1 VALUES (1, 11); -INSERT INTO tt1 VALUES (2, NULL); -CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 ); -INSERT INTO tt2 VALUES (21, 11); -INSERT INTO tt2 VALUES (22, 11); -set enable_hashjoin to off; -set enable_nestloop to off; --- these should give the same results -select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol; - tt1_id | joincol | tt2_id | joincol ---------+---------+--------+--------- - 1 | 11 | 21 | 11 - 1 | 11 | 22 | 11 - 2 | | | -(3 rows) - -select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol; - tt1_id | joincol | tt2_id | joincol ---------+---------+--------+--------- - 1 | 11 | 21 | 11 - 1 | 11 | 22 | 11 - 2 | | | -(3 rows) - -reset enable_hashjoin; -reset enable_nestloop; --- --- regression test for bug #18522 (merge-right-anti-join in inner_unique cases) --- -create temp table tbl_ra(a int unique, b int); -insert into tbl_ra select i, i%100 from generate_series(1,1000)i; -create index on tbl_ra (b); -analyze tbl_ra; -set enable_hashjoin to off; -set enable_nestloop to off; --- ensure we get a merge right anti join -explain (costs off) -select * from tbl_ra t1 -where not exists (select 1 from tbl_ra t2 where t2.b = t1.a) and t1.b < 2; - QUERY PLAN -------------------------------------------------------- - Merge Right Anti Join - Merge Cond: (t2.b = t1.a) - -> Index Only Scan using tbl_ra_b_idx on tbl_ra t2 - -> Sort - Sort Key: t1.a - -> Bitmap Heap Scan on tbl_ra t1 - Recheck Cond: (b < 2) - -> Bitmap Index Scan on tbl_ra_b_idx - Index Cond: (b < 2) -(9 rows) - --- and check we get the expected results -select * from tbl_ra t1 -where not exists (select 1 from tbl_ra t2 where t2.b = t1.a) and t1.b < 2; - a | b -------+--- - 100 | 0 - 101 | 1 - 200 | 0 - 201 | 1 - 300 | 0 - 301 | 1 - 400 | 0 - 401 | 1 - 500 | 0 - 501 | 1 - 600 | 0 - 601 | 1 - 700 | 0 - 701 | 1 - 800 | 0 - 801 | 1 - 900 | 0 - 901 | 1 - 1000 | 0 -(19 rows) - -reset enable_hashjoin; -reset enable_nestloop; --- --- regression test for bug with hash-right-semi join --- -create temp table tbl_rs(a int, b int); -insert into tbl_rs select i, i from generate_series(1,10)i; -analyze tbl_rs; --- ensure we get a hash right semi join -explain (costs off) -select * from tbl_rs t1 join - lateral (select * from tbl_rs t2 where t2.a in - (select t1.a+t3.a from tbl_rs t3) and t2.a < 5) - on true; - QUERY PLAN -------------------------------------------- - Nested Loop - -> Seq Scan on tbl_rs t1 - -> Hash Right Semi Join - Hash Cond: ((t1.a + t3.a) = t2.a) - -> Seq Scan on tbl_rs t3 - -> Hash - -> Seq Scan on tbl_rs t2 - Filter: (a < 5) -(8 rows) - --- and check we get the expected results -select * from tbl_rs t1 join - lateral (select * from tbl_rs t2 where t2.a in - (select t1.a+t3.a from tbl_rs t3) and t2.a < 5) - on true; - a | b | a | b ----+---+---+--- - 1 | 1 | 2 | 2 - 1 | 1 | 3 | 3 - 1 | 1 | 4 | 4 - 2 | 2 | 3 | 3 - 2 | 2 | 4 | 4 - 3 | 3 | 4 | 4 -(6 rows) - --- --- regression test for bug with parallel-hash-right-semi join --- -begin; --- encourage use of parallel plans -set local parallel_setup_cost=0; -set local parallel_tuple_cost=0; -set local min_parallel_table_scan_size=0; -set local max_parallel_workers_per_gather=4; --- ensure we don't get parallel hash right semi join -explain (costs off) -select * from tenk1 t1 -where exists (select 1 from tenk1 t2 where fivethous = t1.fivethous) -and t1.fivethous < 5; - QUERY PLAN --------------------------------------------------- - Gather - Workers Planned: 4 - -> Parallel Hash Semi Join - Hash Cond: (t1.fivethous = t2.fivethous) - -> Parallel Seq Scan on tenk1 t1 - Filter: (fivethous < 5) - -> Parallel Hash - -> Parallel Seq Scan on tenk1 t2 -(8 rows) - -rollback; --- --- regression test for bug #13908 (hash join with skew tuples & nbatch increase) --- -set work_mem to '64kB'; -set enable_mergejoin to off; -set enable_memoize to off; -explain (costs off) -select count(*) from tenk1 a, tenk1 b - where a.hundred = b.thousand and (b.fivethous % 10) < 10; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (a.hundred = b.thousand) - -> Index Only Scan using tenk1_hundred on tenk1 a - -> Hash - -> Seq Scan on tenk1 b - Filter: ((fivethous % 10) < 10) -(7 rows) - -select count(*) from tenk1 a, tenk1 b - where a.hundred = b.thousand and (b.fivethous % 10) < 10; - count --------- - 100000 -(1 row) - -reset work_mem; -reset enable_mergejoin; -reset enable_memoize; --- --- regression test for 8.2 bug with improper re-ordering of left joins --- -create temp table tt3(f1 int, f2 text); -insert into tt3 select x, repeat('xyzzy', 100) from generate_series(1,10000) x; -analyze tt3; -create temp table tt4(f1 int); -insert into tt4 values (0),(1),(9999); -analyze tt4; -set enable_nestloop to off; -EXPLAIN (COSTS OFF) -SELECT a.f1 -FROM tt4 a -LEFT JOIN ( - SELECT b.f1 - FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1) - WHERE COALESCE(c.f1, 0) = 0 -) AS d ON (a.f1 = d.f1) -WHERE COALESCE(d.f1, 0) = 0 -ORDER BY 1; - QUERY PLAN ------------------------------------------------ - Sort - Sort Key: a.f1 - -> Hash Right Join - Hash Cond: (b.f1 = a.f1) - Filter: (COALESCE(b.f1, 0) = 0) - -> Hash Left Join - Hash Cond: (b.f1 = c.f1) - Filter: (COALESCE(c.f1, 0) = 0) - -> Seq Scan on tt3 b - -> Hash - -> Seq Scan on tt3 c - -> Hash - -> Seq Scan on tt4 a -(13 rows) - -SELECT a.f1 -FROM tt4 a -LEFT JOIN ( - SELECT b.f1 - FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1) - WHERE COALESCE(c.f1, 0) = 0 -) AS d ON (a.f1 = d.f1) -WHERE COALESCE(d.f1, 0) = 0 -ORDER BY 1; - f1 ------- - 0 - 1 - 9999 -(3 rows) - -reset enable_nestloop; --- --- test that estimate_hash_bucket_stats estimates correctly with skewed data --- (we should choose to hash the filtered table) --- -create temp table skewedtable (val int not null, filt int not null); -insert into skewedtable -select - case when g <= 100 then 0 else (g % 100) + 1 end, - g % 10 -from generate_series(1, 1000) g; -analyze skewedtable; -explain (costs off) -select * from skewedtable t1 join skewedtable t2 on t1.val = t2.val -where t1.filt = 5; - QUERY PLAN ----------------------------------------- - Hash Join - Hash Cond: (t2.val = t1.val) - -> Seq Scan on skewedtable t2 - -> Hash - -> Seq Scan on skewedtable t1 - Filter: (filt = 5) -(6 rows) - -drop table skewedtable; --- --- basic semijoin and antijoin recognition tests --- -explain (costs off) -select a.* from tenk1 a -where unique1 in (select unique2 from tenk1 b); - QUERY PLAN ------------------------------------------------------------- - Hash Semi Join - Hash Cond: (a.unique1 = b.unique2) - -> Seq Scan on tenk1 a - -> Hash - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - --- sadly, this is not an antijoin -explain (costs off) -select a.* from tenk1 a -where unique1 not in (select unique2 from tenk1 b); - QUERY PLAN ---------------------------------------------------------------- - Seq Scan on tenk1 a - Filter: (NOT (ANY (unique1 = (hashed SubPlan any_1).col1))) - SubPlan any_1 - -> Index Only Scan using tenk1_unique2 on tenk1 b -(4 rows) - -explain (costs off) -select a.* from tenk1 a -where exists (select 1 from tenk1 b where a.unique1 = b.unique2); - QUERY PLAN ------------------------------------------------------------- - Hash Semi Join - Hash Cond: (a.unique1 = b.unique2) - -> Seq Scan on tenk1 a - -> Hash - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - -explain (costs off) -select a.* from tenk1 a -where not exists (select 1 from tenk1 b where a.unique1 = b.unique2); - QUERY PLAN ------------------------------------------------------------- - Hash Anti Join - Hash Cond: (a.unique1 = b.unique2) - -> Seq Scan on tenk1 a - -> Hash - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - -explain (costs off) -select a.* from tenk1 a left join tenk1 b on a.unique1 = b.unique2 -where b.unique2 is null; - QUERY PLAN ------------------------------------------------------------- - Hash Anti Join - Hash Cond: (a.unique1 = b.unique2) - -> Seq Scan on tenk1 a - -> Hash - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - --- check that we avoid de-duplicating columns redundantly -set enable_memoize to off; -explain (costs off) -select 1 from tenk1 -where (hundred, thousand) in (select twothousand, twothousand from onek); - QUERY PLAN -------------------------------------------------- - Hash Join - Hash Cond: (tenk1.hundred = onek.twothousand) - -> Seq Scan on tenk1 - Filter: (hundred = thousand) - -> Hash - -> HashAggregate - Group Key: onek.twothousand - -> Seq Scan on onek -(8 rows) - -reset enable_memoize; --- --- more antijoin recognition tests using NOT NULL constraints --- -begin; -create temp table tbl_anti(a int not null, b int, c int); --- this is an antijoin, as t2.a is non-null for any matching row -explain (costs off) -select * from tenk1 t1 left join tbl_anti t2 on t1.unique1 = t2.b -where t2.a is null; - QUERY PLAN ----------------------------------- - Hash Right Anti Join - Hash Cond: (t2.b = t1.unique1) - -> Seq Scan on tbl_anti t2 - -> Hash - -> Seq Scan on tenk1 t1 -(5 rows) - --- this is an antijoin, as t2.a is non-null for any matching row -explain (costs off) -select * from tenk1 t1 left join - (tbl_anti t2 left join tbl_anti t3 on t2.c = t3.c) on t1.unique1 = t2.b -where t2.a is null; - QUERY PLAN -------------------------------------------- - Hash Right Anti Join - Hash Cond: (t2.b = t1.unique1) - -> Merge Left Join - Merge Cond: (t2.c = t3.c) - -> Sort - Sort Key: t2.c - -> Seq Scan on tbl_anti t2 - -> Sort - Sort Key: t3.c - -> Seq Scan on tbl_anti t3 - -> Hash - -> Seq Scan on tenk1 t1 -(12 rows) - --- this is not an antijoin, as t3.a can be nulled by t2/t3 join -explain (costs off) -select * from tenk1 t1 left join - (tbl_anti t2 left join tbl_anti t3 on t2.c = t3.c) on t1.unique1 = t2.b -where t3.a is null; - QUERY PLAN -------------------------------------------- - Hash Right Join - Hash Cond: (t2.b = t1.unique1) - Filter: (t3.a IS NULL) - -> Merge Left Join - Merge Cond: (t2.c = t3.c) - -> Sort - Sort Key: t2.c - -> Seq Scan on tbl_anti t2 - -> Sort - Sort Key: t3.c - -> Seq Scan on tbl_anti t3 - -> Hash - -> Seq Scan on tenk1 t1 -(13 rows) - -rollback; --- --- regression test for bogus RTE_GROUP entries --- -explain (costs off) -select a.* from tenk1 a -where exists (select 1 from tenk1 b where a.unique1 = b.unique2 group by b.unique1); - QUERY PLAN ------------------------------------------------------------- - Hash Semi Join - Hash Cond: (a.unique1 = b.unique2) - -> Seq Scan on tenk1 a - -> Hash - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - --- --- regression test for proper handling of outer joins within antijoins --- -create temp table tt4x(c1 int, c2 int, c3 int); -explain (costs off) -select * from tt4x t1 -where not exists ( - select 1 from tt4x t2 - left join tt4x t3 on t2.c3 = t3.c1 - left join ( select t5.c1 as c1 - from tt4x t4 left join tt4x t5 on t4.c2 = t5.c1 - ) a1 on t3.c2 = a1.c1 - where t1.c1 = t2.c2 -); - QUERY PLAN ---------------------------------------------------------- - Merge Anti Join - Merge Cond: (t1.c1 = t2.c2) - -> Sort - Sort Key: t1.c1 - -> Seq Scan on tt4x t1 - -> Sort - Sort Key: t2.c2 - -> Merge Right Join - Merge Cond: (t5.c1 = t3.c2) - -> Merge Join - Merge Cond: (t4.c2 = t5.c1) - -> Sort - Sort Key: t4.c2 - -> Seq Scan on tt4x t4 - -> Sort - Sort Key: t5.c1 - -> Seq Scan on tt4x t5 - -> Sort - Sort Key: t3.c2 - -> Merge Left Join - Merge Cond: (t2.c3 = t3.c1) - -> Sort - Sort Key: t2.c3 - -> Seq Scan on tt4x t2 - -> Sort - Sort Key: t3.c1 - -> Seq Scan on tt4x t3 -(27 rows) - --- --- regression test for problems of the sort depicted in bug #3494 --- -create temp table tt5(f1 int, f2 int); -create temp table tt6(f1 int, f2 int); -insert into tt5 values(1, 10); -insert into tt5 values(1, 11); -insert into tt6 values(1, 9); -insert into tt6 values(1, 2); -insert into tt6 values(2, 9); -select * from tt5,tt6 where tt5.f1 = tt6.f1 and tt5.f1 = tt5.f2 - tt6.f2; - f1 | f2 | f1 | f2 -----+----+----+---- - 1 | 10 | 1 | 9 -(1 row) - --- --- regression test for problems of the sort depicted in bug #3588 --- -create temp table xx (pkxx int); -create temp table yy (pkyy int, pkxx int); -insert into xx values (1); -insert into xx values (2); -insert into xx values (3); -insert into yy values (101, 1); -insert into yy values (201, 2); -insert into yy values (301, NULL); -select yy.pkyy as yy_pkyy, yy.pkxx as yy_pkxx, yya.pkyy as yya_pkyy, - xxa.pkxx as xxa_pkxx, xxb.pkxx as xxb_pkxx -from yy - left join (SELECT * FROM yy where pkyy = 101) as yya ON yy.pkyy = yya.pkyy - left join xx xxa on yya.pkxx = xxa.pkxx - left join xx xxb on coalesce (xxa.pkxx, 1) = xxb.pkxx; - yy_pkyy | yy_pkxx | yya_pkyy | xxa_pkxx | xxb_pkxx ----------+---------+----------+----------+---------- - 101 | 1 | 101 | 1 | 1 - 201 | 2 | | | 1 - 301 | | | | 1 -(3 rows) - --- --- regression test for improper pushing of constants across outer-join clauses --- (as seen in early 8.2.x releases) --- -create temp table zt1 (f1 int primary key); -create temp table zt2 (f2 int primary key); -create temp table zt3 (f3 int primary key); -insert into zt1 values(53); -insert into zt2 values(53); -select * from - zt2 left join zt3 on (f2 = f3) - left join zt1 on (f3 = f1) -where f2 = 53; - f2 | f3 | f1 -----+----+---- - 53 | | -(1 row) - -create temp view zv1 as select *,'dummy'::text AS junk from zt1; -select * from - zt2 left join zt3 on (f2 = f3) - left join zv1 on (f3 = f1) -where f2 = 53; - f2 | f3 | f1 | junk -----+----+----+------ - 53 | | | -(1 row) - --- --- regression test for improper extraction of OR indexqual conditions --- (as seen in early 8.3.x releases) --- -select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred -from tenk1 a left join tenk1 b on a.unique2 = b.tenthous -where a.unique1 = 42 and - ((b.unique2 is null and a.ten = 2) or b.hundred = 3); - unique2 | ten | tenthous | unique2 | hundred ----------+-----+----------+---------+--------- -(0 rows) - --- --- test proper positioning of one-time quals in EXISTS (8.4devel bug) --- -prepare foo(bool) as - select count(*) from tenk1 a left join tenk1 b - on (a.unique2 = b.unique1 and exists - (select 1 from tenk1 c where c.thousand = b.unique2 and $1)); -execute foo(true); - count -------- - 10000 -(1 row) - -execute foo(false); - count -------- - 10000 -(1 row) - --- --- test for sane behavior with noncanonical merge clauses, per bug #4926 --- -begin; -set enable_mergejoin = 1; -set enable_hashjoin = 0; -set enable_nestloop = 0; -create temp table a (i integer); -create temp table b (x integer, y integer); -select * from a left join b on i = x and i = y and x = i; - i | x | y ----+---+--- -(0 rows) - -rollback; --- --- test handling of merge clauses using record_ops --- -begin; -create type mycomptype as (id int, v bigint); -create temp table tidv (idv mycomptype); -create index on tidv (idv); -explain (costs off) -select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv; - QUERY PLAN ----------------------------------------------------------- - Merge Join - Merge Cond: (a.idv = b.idv) - -> Index Only Scan using tidv_idv_idx on tidv a - -> Materialize - -> Index Only Scan using tidv_idv_idx on tidv b -(5 rows) - -set enable_mergejoin = 0; -set enable_hashjoin = 0; -explain (costs off) -select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv; - QUERY PLAN ----------------------------------------------------- - Nested Loop - -> Seq Scan on tidv a - -> Index Only Scan using tidv_idv_idx on tidv b - Index Cond: (idv = a.idv) -(4 rows) - -rollback; --- --- test NULL behavior of whole-row Vars, per bug #5025 --- -select t1.q2, count(t2.*) -from int8_tbl t1 left join int8_tbl t2 on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - -select t1.q2, count(t2.*) -from int8_tbl t1 left join (select * from int8_tbl) t2 on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - -select t1.q2, count(t2.*) -from int8_tbl t1 left join (select * from int8_tbl offset 0) t2 on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - -select t1.q2, count(t2.*) -from int8_tbl t1 left join - (select q1, case when q2=1 then 1 else q2 end as q2 from int8_tbl) t2 - on (t1.q2 = t2.q1) -group by t1.q2 order by 1; - q2 | count --------------------+------- - -4567890123456789 | 0 - 123 | 2 - 456 | 0 - 4567890123456789 | 6 -(4 rows) - --- --- test incorrect failure to NULL pulled-up subexpressions --- -begin; -create temp table a ( - code char not null, - constraint a_pk primary key (code) -); -create temp table b ( - a char not null, - num integer not null, - constraint b_pk primary key (a, num) -); -create temp table c ( - name char not null, - a char, - constraint c_pk primary key (name) -); -insert into a (code) values ('p'); -insert into a (code) values ('q'); -insert into b (a, num) values ('p', 1); -insert into b (a, num) values ('p', 2); -insert into c (name, a) values ('A', 'p'); -insert into c (name, a) values ('B', 'q'); -insert into c (name, a) values ('C', null); -select c.name, ss.code, ss.b_cnt, ss.const -from c left join - (select a.code, coalesce(b_grp.cnt, 0) as b_cnt, -1 as const - from a left join - (select count(1) as cnt, b.a from b group by b.a) as b_grp - on a.code = b_grp.a - ) as ss - on (c.a = ss.code) -order by c.name; - name | code | b_cnt | const -------+------+-------+------- - A | p | 2 | -1 - B | q | 0 | -1 - C | | | -(3 rows) - -rollback; --- --- test incorrect handling of placeholders that only appear in targetlists, --- per bug #6154 --- -SELECT * FROM -( SELECT 1 as key1 ) sub1 -LEFT JOIN -( SELECT sub3.key3, sub4.value2, COALESCE(sub4.value2, 66) as value3 FROM - ( SELECT 1 as key3 ) sub3 - LEFT JOIN - ( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM - ( SELECT 1 as key5 ) sub5 - LEFT JOIN - ( SELECT 2 as key6, 42 as value1 ) sub6 - ON sub5.key5 = sub6.key6 - ) sub4 - ON sub4.key5 = sub3.key3 -) sub2 -ON sub1.key1 = sub2.key3; - key1 | key3 | value2 | value3 -------+------+--------+-------- - 1 | 1 | 1 | 1 -(1 row) - --- test the path using join aliases, too -SELECT * FROM -( SELECT 1 as key1 ) sub1 -LEFT JOIN -( SELECT sub3.key3, value2, COALESCE(value2, 66) as value3 FROM - ( SELECT 1 as key3 ) sub3 - LEFT JOIN - ( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM - ( SELECT 1 as key5 ) sub5 - LEFT JOIN - ( SELECT 2 as key6, 42 as value1 ) sub6 - ON sub5.key5 = sub6.key6 - ) sub4 - ON sub4.key5 = sub3.key3 -) sub2 -ON sub1.key1 = sub2.key3; - key1 | key3 | value2 | value3 -------+------+--------+-------- - 1 | 1 | 1 | 1 -(1 row) - --- --- test case where a PlaceHolderVar is used as a nestloop parameter --- -EXPLAIN (COSTS OFF) -SELECT qq, unique1 - FROM - ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1 - FULL OUTER JOIN - ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 - USING (qq) - INNER JOIN tenk1 c ON qq = unique2; - QUERY PLAN ---------------------------------------------------------------------------------------------------------- - Nested Loop - -> Hash Full Join - Hash Cond: ((COALESCE(a.q1, '0'::bigint)) = (COALESCE(b.q2, '-1'::bigint))) - -> Seq Scan on int8_tbl a - -> Hash - -> Seq Scan on int8_tbl b - -> Index Scan using tenk1_unique2 on tenk1 c - Index Cond: (unique2 = COALESCE((COALESCE(a.q1, '0'::bigint)), (COALESCE(b.q2, '-1'::bigint)))) -(8 rows) - -SELECT qq, unique1 - FROM - ( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1 - FULL OUTER JOIN - ( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2 - USING (qq) - INNER JOIN tenk1 c ON qq = unique2; - qq | unique1 ------+--------- - 123 | 4596 - 123 | 4596 - 456 | 7318 -(3 rows) - --- --- nested nestloops can require nested PlaceHolderVars --- -create temp table nt1 ( - id int primary key, - a1 boolean, - a2 boolean -); -create temp table nt2 ( - id int primary key, - nt1_id int, - b1 boolean, - b2 boolean, - foreign key (nt1_id) references nt1(id) -); -create temp table nt3 ( - id int primary key, - nt2_id int, - c1 boolean, - foreign key (nt2_id) references nt2(id) -); -insert into nt1 values (1,true,true); -insert into nt1 values (2,true,false); -insert into nt1 values (3,false,false); -insert into nt2 values (1,1,true,true); -insert into nt2 values (2,2,true,false); -insert into nt2 values (3,3,false,false); -insert into nt3 values (1,1,true); -insert into nt3 values (2,2,false); -insert into nt3 values (3,3,true); -explain (costs off) -select nt3.id -from nt3 as nt3 - left join - (select nt2.*, (nt2.b1 and ss1.a3) AS b3 - from nt2 as nt2 - left join - (select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1 - on ss1.id = nt2.nt1_id - ) as ss2 - on ss2.id = nt3.nt2_id -where nt3.id = 1 and ss2.b3; - QUERY PLAN ----------------------------------------------- - Nested Loop - -> Nested Loop - -> Index Scan using nt3_pkey on nt3 - Index Cond: (id = 1) - -> Index Scan using nt2_pkey on nt2 - Index Cond: (id = nt3.nt2_id) - -> Index Only Scan using nt1_pkey on nt1 - Index Cond: (id = nt2.nt1_id) - Filter: (nt2.b1 AND true) -(9 rows) - -select nt3.id -from nt3 as nt3 - left join - (select nt2.*, (nt2.b1 and ss1.a3) AS b3 - from nt2 as nt2 - left join - (select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1 - on ss1.id = nt2.nt1_id - ) as ss2 - on ss2.id = nt3.nt2_id -where nt3.id = 1 and ss2.b3; - id ----- - 1 -(1 row) - --- --- test case where a PlaceHolderVar is propagated into a subquery --- -explain (costs off) -select * from - int8_tbl t1 left join - (select q1 as x, 42 as y from int8_tbl t2) ss - on t1.q2 = ss.x -where - 1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) -order by 1,2; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: t1.q1, t1.q2 - -> Hash Left Join - Hash Cond: (t1.q2 = t2.q1) - Filter: (1 = (SubPlan expr_1)) - -> Seq Scan on int8_tbl t1 - -> Hash - -> Seq Scan on int8_tbl t2 - SubPlan expr_1 - -> Limit - -> Result - One-Time Filter: ((42) IS NOT NULL) - -> Seq Scan on int8_tbl t3 -(13 rows) - -select * from - int8_tbl t1 left join - (select q1 as x, 42 as y from int8_tbl t2) ss - on t1.q2 = ss.x -where - 1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1) -order by 1,2; - q1 | q2 | x | y -------------------+------------------+------------------+---- - 123 | 4567890123456789 | 4567890123456789 | 42 - 123 | 4567890123456789 | 4567890123456789 | 42 - 123 | 4567890123456789 | 4567890123456789 | 42 - 4567890123456789 | 123 | 123 | 42 - 4567890123456789 | 123 | 123 | 42 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 42 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 42 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 42 -(8 rows) - --- --- variant where a PlaceHolderVar is needed at a join, but not above the join --- -explain (costs off) -select * from - int4_tbl as i41, - lateral - (select 1 as x from - (select i41.f1 as lat, - i42.f1 as loc from - int8_tbl as i81, int4_tbl as i42) as ss1 - right join int4_tbl as i43 on (i43.f1 > 1) - where ss1.loc = ss1.lat) as ss2 -where i41.f1 > 0; - QUERY PLAN --------------------------------------------------- - Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl i41 - Filter: (f1 > 0) - -> Nested Loop - Join Filter: (i42.f1 = i41.f1) - -> Seq Scan on int8_tbl i81 - -> Materialize - -> Seq Scan on int4_tbl i42 - -> Materialize - -> Seq Scan on int4_tbl i43 - Filter: (f1 > 1) -(12 rows) - -select * from - int4_tbl as i41, - lateral - (select 1 as x from - (select i41.f1 as lat, - i42.f1 as loc from - int8_tbl as i81, int4_tbl as i42) as ss1 - right join int4_tbl as i43 on (i43.f1 > 1) - where ss1.loc = ss1.lat) as ss2 -where i41.f1 > 0; - f1 | x -------------+--- - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 123456 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 - 2147483647 | 1 -(20 rows) - --- --- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE --- -select * from int4_tbl a full join int4_tbl b on true; - f1 | f1 --------------+------------- - 0 | 0 - 0 | 123456 - 0 | -123456 - 0 | 2147483647 - 0 | -2147483647 - 123456 | 0 - 123456 | 123456 - 123456 | -123456 - 123456 | 2147483647 - 123456 | -2147483647 - -123456 | 0 - -123456 | 123456 - -123456 | -123456 - -123456 | 2147483647 - -123456 | -2147483647 - 2147483647 | 0 - 2147483647 | 123456 - 2147483647 | -123456 - 2147483647 | 2147483647 - 2147483647 | -2147483647 - -2147483647 | 0 - -2147483647 | 123456 - -2147483647 | -123456 - -2147483647 | 2147483647 - -2147483647 | -2147483647 -(25 rows) - -select * from int4_tbl a full join int4_tbl b on false; - f1 | f1 --------------+------------- - | 0 - | 123456 - | -123456 - | 2147483647 - | -2147483647 - 0 | - 123456 | - -123456 | - 2147483647 | - -2147483647 | -(10 rows) - --- --- test for ability to use a cartesian join when necessary --- -create temp table q1 as select 1 as q1; -create temp table q2 as select 0 as q2; -analyze q1; -analyze q2; -explain (costs off) -select * from - tenk1 join int4_tbl on f1 = twothousand, - q1, q2 -where q1 = thousand or q2 = thousand; - QUERY PLAN ------------------------------------------------------------------------- - Hash Join - Hash Cond: (tenk1.twothousand = int4_tbl.f1) - -> Nested Loop - -> Nested Loop - -> Seq Scan on q1 - -> Seq Scan on q2 - -> Bitmap Heap Scan on tenk1 - Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand)) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = ANY (ARRAY[q1.q1, q2.q2])) - -> Hash - -> Seq Scan on int4_tbl -(12 rows) - -explain (costs off) -select * from - tenk1 join int4_tbl on f1 = twothousand, - q1, q2 -where thousand = (q1 + q2); - QUERY PLAN --------------------------------------------------------------- - Hash Join - Hash Cond: (tenk1.twothousand = int4_tbl.f1) - -> Nested Loop - -> Nested Loop - -> Seq Scan on q1 - -> Seq Scan on q2 - -> Bitmap Heap Scan on tenk1 - Recheck Cond: (thousand = (q1.q1 + q2.q2)) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = (q1.q1 + q2.q2)) - -> Hash - -> Seq Scan on int4_tbl -(12 rows) - --- --- test ability to generate a suitable plan for a star-schema query --- -explain (costs off) -select * from - tenk1, int8_tbl a, int8_tbl b -where thousand = a.q1 and tenthous = b.q1 and a.q2 = 1 and b.q2 = 2; - QUERY PLAN ---------------------------------------------------------------------- - Nested Loop - -> Seq Scan on int8_tbl b - Filter: (q2 = 2) - -> Nested Loop - -> Seq Scan on int8_tbl a - Filter: (q2 = 1) - -> Index Scan using tenk1_thous_tenthous on tenk1 - Index Cond: ((thousand = a.q1) AND (tenthous = b.q1)) -(8 rows) - --- --- test a corner case in which we shouldn't apply the star-schema optimization --- -explain (costs off) -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (select 1,0 from onerow) v1(x1,x2) - left join (select 3,1 from onerow) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - QUERY PLAN ------------------------------------------------------------------------ - Nested Loop - -> Nested Loop - Join Filter: (t1.stringu1 > t2.stringu2) - -> Nested Loop - -> Nested Loop - -> Seq Scan on onerow - -> Seq Scan on onerow onerow_1 - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: ((unique2 = (11)) AND (unique2 < 42)) - -> Index Scan using tenk1_unique1 on tenk1 t2 - Index Cond: (unique1 = (3)) - -> Seq Scan on int4_tbl i1 - Filter: (f1 = 0) -(13 rows) - -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (select 1,0 from onerow) v1(x1,x2) - left join (select 3,1 from onerow) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - unique2 | stringu1 | unique1 | stringu2 ----------+----------+---------+---------- - 11 | WFAAAA | 3 | LKIAAA -(1 row) - --- variant that isn't quite a star-schema case -explain (verbose, costs off) -select ss1.d1 from - tenk1 as t1 - inner join tenk1 as t2 - on t1.tenthous = t2.ten - inner join - int8_tbl as i8 - left join int4_tbl as i4 - inner join (select 64::information_schema.cardinal_number as d1 - from tenk1 t3, - lateral (select abs(t3.unique1) + random()) ss0(x) - where t3.fivethous < 0) as ss1 - on i4.f1 = ss1.d1 - on i8.q1 = i4.f1 - on t1.tenthous = ss1.d1 -where t1.unique1 < i4.f1; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: (64)::information_schema.cardinal_number - Join Filter: (t1.tenthous = ((64)::information_schema.cardinal_number)::integer) - -> Seq Scan on public.tenk1 t3 - Output: t3.unique1, t3.unique2, t3.two, t3.four, t3.ten, t3.twenty, t3.hundred, t3.thousand, t3.twothousand, t3.fivethous, t3.tenthous, t3.odd, t3.even, t3.stringu1, t3.stringu2, t3.string4 - Filter: (t3.fivethous < 0) - -> Nested Loop - Output: t1.tenthous, t2.ten - -> Nested Loop - Output: t1.tenthous, t2.ten, i4.f1 - Join Filter: (t1.unique1 < i4.f1) - -> Hash Join - Output: t1.tenthous, t1.unique1, t2.ten - Hash Cond: (t2.ten = t1.tenthous) - -> Seq Scan on public.tenk1 t2 - Output: t2.unique1, t2.unique2, t2.two, t2.four, t2.ten, t2.twenty, t2.hundred, t2.thousand, t2.twothousand, t2.fivethous, t2.tenthous, t2.odd, t2.even, t2.stringu1, t2.stringu2, t2.string4 - -> Hash - Output: t1.tenthous, t1.unique1 - -> Nested Loop - Output: t1.tenthous, t1.unique1 - -> Subquery Scan on ss0 - Output: ss0.x, (64)::information_schema.cardinal_number - -> Result - Output: ((abs(t3.unique1))::double precision + random()) - -> Index Scan using tenk1_thous_tenthous on public.tenk1 t1 - Output: t1.unique1, t1.unique2, t1.two, t1.four, t1.ten, t1.twenty, t1.hundred, t1.thousand, t1.twothousand, t1.fivethous, t1.tenthous, t1.odd, t1.even, t1.stringu1, t1.stringu2, t1.string4 - Index Cond: (t1.tenthous = (((64)::information_schema.cardinal_number))::integer) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - Filter: (i4.f1 = ((64)::information_schema.cardinal_number)::integer) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q1 = ((64)::information_schema.cardinal_number)::integer) -(33 rows) - -select ss1.d1 from - tenk1 as t1 - inner join tenk1 as t2 - on t1.tenthous = t2.ten - inner join - int8_tbl as i8 - left join int4_tbl as i4 - inner join (select 64::information_schema.cardinal_number as d1 - from tenk1 t3, - lateral (select abs(t3.unique1) + random()) ss0(x) - where t3.fivethous < 0) as ss1 - on i4.f1 = ss1.d1 - on i8.q1 = i4.f1 - on t1.tenthous = ss1.d1 -where t1.unique1 < i4.f1; - d1 ----- -(0 rows) - --- this variant is foldable by the remove-useless-RESULT-RTEs code -explain (costs off) -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (values(1,0)) v1(x1,x2) - left join (values(3,1)) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - QUERY PLAN ------------------------------------------------------------------ - Nested Loop - Join Filter: (t1.stringu1 > t2.stringu2) - -> Nested Loop - -> Seq Scan on int4_tbl i1 - Filter: (f1 = 0) - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: ((unique2 = (11)) AND (unique2 < 42)) - -> Index Scan using tenk1_unique1 on tenk1 t2 - Index Cond: (unique1 = (3)) -(9 rows) - -select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from - tenk1 t1 - inner join int4_tbl i1 - left join (select v1.x2, v2.y1, 11 AS d1 - from (values(1,0)) v1(x1,x2) - left join (values(3,1)) v2(y1,y2) - on v1.x1 = v2.y2) subq1 - on (i1.f1 = subq1.x2) - on (t1.unique2 = subq1.d1) - left join tenk1 t2 - on (subq1.y1 = t2.unique1) -where t1.unique2 < 42 and t1.stringu1 > t2.stringu2; - unique2 | stringu1 | unique1 | stringu2 ----------+----------+---------+---------- - 11 | WFAAAA | 3 | LKIAAA -(1 row) - --- Here's a variant that we can't fold too aggressively, though, --- or we end up with noplace to evaluate the lateral PHV -explain (verbose, costs off) -select * from - (select 1 as x) ss1 left join (select 2 as y) ss2 on (true), - lateral (select ss2.y as z limit 1) ss3; - QUERY PLAN ---------------------------- - Nested Loop - Output: 1, (2), ((2)) - -> Result - Output: 2 - -> Limit - Output: ((2)) - -> Result - Output: (2) -(8 rows) - -select * from - (select 1 as x) ss1 left join (select 2 as y) ss2 on (true), - lateral (select ss2.y as z limit 1) ss3; - x | y | z ----+---+--- - 1 | 2 | 2 -(1 row) - --- This example demonstrates the folly of our old "have_dangerous_phv" logic -begin; -set local from_collapse_limit to 2; -explain (verbose, costs off) -select * from int8_tbl t1 - left join - (select coalesce(t2.q1 + x, 0) from int8_tbl t2, - lateral (select t3.q1 as x from int8_tbl t3, - lateral (select t2.q1, t3.q1 offset 0) s)) - on true; - QUERY PLAN ------------------------------------------------------------------- - Nested Loop Left Join - Output: t1.q1, t1.q2, (COALESCE((t2.q1 + t3.q1), '0'::bigint)) - -> Seq Scan on public.int8_tbl t1 - Output: t1.q1, t1.q2 - -> Materialize - Output: (COALESCE((t2.q1 + t3.q1), '0'::bigint)) - -> Nested Loop - Output: COALESCE((t2.q1 + t3.q1), '0'::bigint) - -> Seq Scan on public.int8_tbl t2 - Output: t2.q1, t2.q2 - -> Nested Loop - Output: t3.q1 - -> Seq Scan on public.int8_tbl t3 - Output: t3.q1, t3.q2 - -> Result - Output: NULL::bigint, NULL::bigint -(16 rows) - -rollback; --- ... not that the initial replacement didn't have some bugs too -begin; -create temp table t(i int primary key); -explain (verbose, costs off) -select * from t t1 - left join (select 1 as x, * from t t2(i2)) t2ss on t1.i = t2ss.i2 - left join t t3(i3) on false - left join t t4(i4) on t4.i4 > t2ss.x; - QUERY PLAN ----------------------------------------------------------- - Nested Loop Left Join - Output: t1.i, (1), t2.i2, t3.i3, t4.i4 - -> Nested Loop Left Join - Output: t1.i, t2.i2, (1), t3.i3 - Join Filter: false - -> Hash Left Join - Output: t1.i, t2.i2, (1) - Inner Unique: true - Hash Cond: (t1.i = t2.i2) - -> Seq Scan on pg_temp.t t1 - Output: t1.i - -> Hash - Output: t2.i2, (1) - -> Seq Scan on pg_temp.t t2 - Output: t2.i2, 1 - -> Result - Output: t3.i3 - Replaces: Scan on t3 - One-Time Filter: false - -> Memoize - Output: t4.i4 - Cache Key: (1) - Cache Mode: binary - -> Index Only Scan using t_pkey on pg_temp.t t4 - Output: t4.i4 - Index Cond: (t4.i4 > (1)) -(26 rows) - -explain (verbose, costs off) -select * from - (select k from - (select i, coalesce(i, j) as k from - (select i from t union all select 0) - join (select 1 as j limit 1) on i = j) - right join (select 2 as x) on true - join (select 3 as y) on i is not null - ), - lateral (select k as kl limit 1); - QUERY PLAN -------------------------------------------------------------------- - Nested Loop - Output: COALESCE(t.i, (1)), ((COALESCE(t.i, (1)))) - -> Limit - Output: 1 - -> Result - Output: 1 - -> Nested Loop - Output: t.i, ((COALESCE(t.i, (1)))) - -> Result - Output: t.i, COALESCE(t.i, (1)) - -> Append - -> Index Only Scan using t_pkey on pg_temp.t - Output: t.i - Index Cond: (t.i = (1)) - -> Result - Output: 0 - One-Time Filter: ((1) = 0) - -> Limit - Output: ((COALESCE(t.i, (1)))) - -> Result - Output: (COALESCE(t.i, (1))) -(21 rows) - -rollback; --- PHVs containing SubLinks are quite tricky to get right -explain (verbose, costs off) -select * -from int8_tbl i8 - inner join - (select (select true) as x - from int4_tbl i4, lateral (select i4.f1 as y limit 1) ss1 - where i4.f1 = 0) ss2 on true - right join (select false as z) ss3 on true, - lateral (select i8.q2 as q2l where x limit 1) ss4 -where i8.q2 = 123; - QUERY PLAN ---------------------------------------------------------------------- - Nested Loop - Output: i8.q1, i8.q2, (InitPlan expr_1).col1, false, (i8.q2) - InitPlan expr_1 - -> Result - Output: true - InitPlan expr_2 - -> Result - Output: true - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - Filter: (i4.f1 = 0) - -> Nested Loop - Output: i8.q1, i8.q2, (i8.q2) - -> Subquery Scan on ss1 - Output: ss1.y, (InitPlan expr_1).col1 - -> Limit - Output: NULL::integer - -> Result - Output: NULL::integer - -> Nested Loop - Output: i8.q1, i8.q2, (i8.q2) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 123) - -> Limit - Output: (i8.q2) - -> Result - Output: i8.q2 - One-Time Filter: ((InitPlan expr_1).col1) -(29 rows) - -explain (verbose, costs off) -select * -from int8_tbl i8 - inner join - (select (select true) as x - from int4_tbl i4, lateral (select 1 as y limit 1) ss1 - where i4.f1 = 0) ss2 on true - right join (select false as z) ss3 on true, - lateral (select i8.q2 as q2l where x limit 1) ss4 -where i8.q2 = 123; - QUERY PLAN ---------------------------------------------------------------------- - Nested Loop - Output: i8.q1, i8.q2, (InitPlan expr_1).col1, false, (i8.q2) - InitPlan expr_1 - -> Result - Output: true - InitPlan expr_2 - -> Result - Output: true - -> Limit - Output: NULL::integer - -> Result - Output: NULL::integer - -> Nested Loop - Output: i8.q1, i8.q2, (i8.q2) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1, (InitPlan expr_1).col1 - Filter: (i4.f1 = 0) - -> Nested Loop - Output: i8.q1, i8.q2, (i8.q2) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 123) - -> Limit - Output: (i8.q2) - -> Result - Output: i8.q2 - One-Time Filter: ((InitPlan expr_1).col1) -(27 rows) - --- Test proper handling of appendrel PHVs during useless-RTE removal -explain (costs off) -select * from - (select 0 as z) as t1 - left join - (select true as a) as t2 - on true, - lateral (select true as b - union all - select a as b) as t3 -where b; - QUERY PLAN ---------------------------------------- - Nested Loop - -> Result - -> Append - -> Result - -> Result - One-Time Filter: (true) -(6 rows) - -select * from - (select 0 as z) as t1 - left join - (select true as a) as t2 - on true, - lateral (select true as b - union all - select a as b) as t3 -where b; - z | a | b ----+---+--- - 0 | t | t - 0 | t | t -(2 rows) - --- Test PHV in a semijoin qual, which confused useless-RTE removal (bug #17700) -explain (verbose, costs off) -with ctetable as not materialized ( select 1 as f1 ) -select * from ctetable c1 -where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true ); - QUERY PLAN ----------------------------- - Result - Output: 1 - One-Time Filter: (1 = 1) -(3 rows) - -with ctetable as not materialized ( select 1 as f1 ) -select * from ctetable c1 -where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true ); - f1 ----- - 1 -(1 row) - --- Test PHV that winds up in a Result node, despite having nonempty nullingrels -explain (verbose, costs off) -select table_catalog, table_name -from int4_tbl t1 - inner join (int8_tbl t2 - left join information_schema.column_udt_usage on null) - on null; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------- - Result - Output: (current_database())::information_schema.sql_identifier, (c.relname)::information_schema.sql_identifier - Replaces: Join on t1, t2, a, c, nc, t, nt, bt, nbt - One-Time Filter: false -(4 rows) - --- Test handling of qual pushdown to appendrel members with non-Var outputs -explain (verbose, costs off) -select * from int4_tbl left join ( - select text 'foo' union all select text 'bar' -) ss(x) on true -where ss.x is null; - QUERY PLAN ------------------------------------------ - Nested Loop Left Join - Output: int4_tbl.f1, ('foo'::text) - Filter: (('foo'::text) IS NULL) - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 - -> Materialize - Output: ('foo'::text) - -> Append - -> Result - Output: 'foo'::text - -> Result - Output: 'bar'::text -(12 rows) - --- Test computation of varnullingrels when translating appendrel Var -begin; -create temp table t_append (a int not null, b int); -insert into t_append values (1, 1); -insert into t_append values (2, 3); -explain (verbose, costs off) -select t1.a, s.a from t_append t1 - left join t_append t2 on t1.a = t2.b - join lateral ( - select t1.a as a union all select t2.a as a - ) s on true -where s.a is not null; - QUERY PLAN ---------------------------------------------------- - Nested Loop - Output: t1.a, (t1.a) - -> Merge Left Join - Output: t1.a, t2.a - Merge Cond: (t1.a = t2.b) - -> Sort - Output: t1.a - Sort Key: t1.a - -> Seq Scan on pg_temp.t_append t1 - Output: t1.a - -> Sort - Output: t2.a, t2.b - Sort Key: t2.b - -> Seq Scan on pg_temp.t_append t2 - Output: t2.a, t2.b - -> Append - -> Result - Output: t1.a - -> Result - Output: t2.a - One-Time Filter: (t2.a IS NOT NULL) -(21 rows) - -select t1.a, s.a from t_append t1 - left join t_append t2 on t1.a = t2.b - join lateral ( - select t1.a as a union all select t2.a as a - ) s on true -where s.a is not null; - a | a ----+--- - 1 | 1 - 1 | 1 - 2 | 2 -(3 rows) - -rollback; --- --- test inlining of immutable functions --- -create function f_immutable_int4(i integer) returns integer as -$$ begin return i; end; $$ language plpgsql immutable; --- check optimization of function scan with join -explain (costs off) -select unique1 from tenk1, (select * from f_immutable_int4(1) x) x -where x = unique1; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (verbose, costs off) -select unique1, x.* -from tenk1, (select *, random() from f_immutable_int4(1) x) x -where x = unique1; - QUERY PLAN ------------------------------------------------------------ - Nested Loop - Output: tenk1.unique1, (1), (random()) - -> Result - Output: 1, random() - -> Index Only Scan using tenk1_unique1 on public.tenk1 - Output: tenk1.unique1 - Index Cond: (tenk1.unique1 = (1)) -(7 rows) - -explain (costs off) -select unique1 from tenk1, f_immutable_int4(1) x where x = unique1; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (costs off) -select unique1 from tenk1, lateral f_immutable_int4(1) x where x = unique1; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (costs off) -select unique1 from tenk1, lateral f_immutable_int4(1) x where x in (select 17); - QUERY PLAN ---------------------------- - Result - Replaces: Scan on tenk1 - One-Time Filter: false -(3 rows) - -explain (costs off) -select unique1, x from tenk1 join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------- - Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(2 rows) - -explain (costs off) -select unique1, x from tenk1 left join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------------- - Nested Loop Left Join - Join Filter: (tenk1.unique1 = 1) - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Materialize - -> Result -(5 rows) - -explain (costs off) -select unique1, x from tenk1 right join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------------- - Nested Loop Left Join - -> Result - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(4 rows) - -explain (costs off) -select unique1, x from tenk1 full join f_immutable_int4(1) x on unique1 = x; - QUERY PLAN ----------------------------------------------------- - Merge Full Join - Merge Cond: (tenk1.unique1 = (1)) - -> Index Only Scan using tenk1_unique1 on tenk1 - -> Sort - Sort Key: (1) - -> Result -(6 rows) - --- check that pullup of a const function allows further const-folding -explain (costs off) -select unique1 from tenk1, f_immutable_int4(1) x where x = 42; - QUERY PLAN ---------------------------- - Result - Replaces: Scan on tenk1 - One-Time Filter: false -(3 rows) - --- test inlining of immutable functions with PlaceHolderVars -explain (costs off) -select nt3.id -from nt3 as nt3 - left join - (select nt2.*, (nt2.b1 or i4 = 42) AS b3 - from nt2 as nt2 - left join - f_immutable_int4(0) i4 - on i4 = nt2.nt1_id - ) as ss2 - on ss2.id = nt3.nt2_id -where nt3.id = 1 and ss2.b3; - QUERY PLAN ----------------------------------------------- - Nested Loop Left Join - Filter: ((nt2.b1 OR ((0) = 42))) - -> Index Scan using nt3_pkey on nt3 - Index Cond: (id = 1) - -> Nested Loop Left Join - Join Filter: (0 = nt2.nt1_id) - -> Index Scan using nt2_pkey on nt2 - Index Cond: (id = nt3.nt2_id) - -> Result -(9 rows) - -drop function f_immutable_int4(int); --- test inlining when function returns composite -create function mki8(bigint, bigint) returns int8_tbl as -$$select row($1,$2)::int8_tbl$$ language sql; -create function mki4(int) returns int4_tbl as -$$select row($1)::int4_tbl$$ language sql; -explain (verbose, costs off) -select * from mki8(1,2); - QUERY PLAN ------------------------------------- - Function Scan on mki8 - Output: q1, q2 - Function Call: '(1,2)'::int8_tbl -(3 rows) - -select * from mki8(1,2); - q1 | q2 -----+---- - 1 | 2 -(1 row) - -explain (verbose, costs off) -select * from mki4(42); - QUERY PLAN ------------------------------------ - Function Scan on mki4 - Output: f1 - Function Call: '(42)'::int4_tbl -(3 rows) - -select * from mki4(42); - f1 ----- - 42 -(1 row) - -drop function mki8(bigint, bigint); -drop function mki4(int); --- test const-folding of a whole-row Var into a per-field Var --- (need to inline a function to reach this case, else parser does it) -create function f_field_select(t onek) returns int4 as -$$ select t.unique2; $$ language sql immutable; -explain (verbose, costs off) -select (t2.*).unique1, f_field_select(t2) from tenk1 t1 - left join onek t2 on t1.unique1 = t2.unique1 - left join int8_tbl t3 on true; - QUERY PLAN --------------------------------------------------------------------- - Nested Loop Left Join - Output: t2.unique1, t2.unique2 - -> Hash Left Join - Output: t2.unique1, t2.unique2 - Hash Cond: (t1.unique1 = t2.unique1) - -> Index Only Scan using tenk1_unique1 on public.tenk1 t1 - Output: t1.unique1 - -> Hash - Output: t2.unique1, t2.unique2 - -> Seq Scan on public.onek t2 - Output: t2.unique1, t2.unique2 - -> Materialize - -> Seq Scan on public.int8_tbl t3 -(13 rows) - -drop function f_field_select(t onek); --- --- test extraction of restriction OR clauses from join OR clause --- (we used to only do this for indexable clauses) --- -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.hundred = 4); - QUERY PLAN -------------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = 3) AND (b.hundred = 4))) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: ((unique1 = 2) OR (hundred = 4)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 2) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = 4) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR (unique2 = 3)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = 3) -(17 rows) - -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.ten = 4); - QUERY PLAN ---------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR ((a.unique2 = 3) AND (b.ten = 4))) - -> Seq Scan on tenk1 b - Filter: ((unique1 = 2) OR (ten = 4)) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR (unique2 = 3)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = 3) -(12 rows) - -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or - ((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4); - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4))) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: ((unique1 = 2) OR (hundred = 4)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 2) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = 4) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7))) - Filter: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = ANY ('{3,7}'::integer[])) -(18 rows) - -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 = 1 and b.unique1 = 2) or - ((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4); - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4))) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: ((unique1 = 2) OR (hundred = 4)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 2) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = 4) - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7))) - Filter: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = 1) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = ANY ('{3,7}'::integer[])) -(18 rows) - -explain (costs off) -select * from tenk1 a join tenk1 b on - (a.unique1 < 20 or a.unique1 = 3 or a.unique1 = 1 and b.unique1 = 2) or - ((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4); - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: ((a.unique1 < 20) OR (a.unique1 = 3) OR ((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4))) - -> Seq Scan on tenk1 b - -> Materialize - -> Bitmap Heap Scan on tenk1 a - Recheck Cond: ((unique1 < 20) OR ((unique1 = 3) OR (unique1 = 1)) OR ((unique2 = 3) OR (unique2 = 7))) - Filter: ((unique1 < 20) OR (unique1 = 3) OR (unique1 = 1) OR (unique2 = 3) OR (unique2 = 7)) - -> BitmapOr - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 < 20) - -> Bitmap Index Scan on tenk1_unique1 - Index Cond: (unique1 = ANY ('{3,1}'::integer[])) - -> Bitmap Index Scan on tenk1_unique2 - Index Cond: (unique2 = ANY ('{3,7}'::integer[])) -(14 rows) - --- --- test placement of movable quals in a parameterized join tree --- -explain (costs off) -select * from tenk1 t1 left join - (tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2) - on t1.hundred = t2.hundred and t1.ten = t3.ten -where t1.unique1 = 1; - QUERY PLAN --------------------------------------------------------- - Nested Loop Left Join - -> Index Scan using tenk1_unique1 on tenk1 t1 - Index Cond: (unique1 = 1) - -> Nested Loop - Join Filter: (t1.ten = t3.ten) - -> Bitmap Heap Scan on tenk1 t2 - Recheck Cond: (t1.hundred = hundred) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = t1.hundred) - -> Index Scan using tenk1_unique2 on tenk1 t3 - Index Cond: (unique2 = t2.thousand) -(11 rows) - -explain (costs off) -select * from tenk1 t1 left join - (tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2) - on t1.hundred = t2.hundred and t1.ten + t2.ten = t3.ten -where t1.unique1 = 1; - QUERY PLAN --------------------------------------------------------- - Nested Loop Left Join - -> Index Scan using tenk1_unique1 on tenk1 t1 - Index Cond: (unique1 = 1) - -> Nested Loop - Join Filter: ((t1.ten + t2.ten) = t3.ten) - -> Bitmap Heap Scan on tenk1 t2 - Recheck Cond: (t1.hundred = hundred) - -> Bitmap Index Scan on tenk1_hundred - Index Cond: (hundred = t1.hundred) - -> Index Scan using tenk1_unique2 on tenk1 t3 - Index Cond: (unique2 = t2.thousand) -(11 rows) - -explain (costs off) -select count(*) from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand - join int4_tbl on b.thousand = f1; - QUERY PLAN -------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: (a.unique2 = b.unique1) - -> Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: (thousand = int4_tbl.f1) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = int4_tbl.f1) - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = b.unique2) - -> Index Only Scan using tenk1_thous_tenthous on tenk1 c - Index Cond: (thousand = a.thousand) -(14 rows) - -select count(*) from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand - join int4_tbl on b.thousand = f1; - count -------- - 10 -(1 row) - -explain (costs off) -select b.unique1 from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand - join int4_tbl i1 on b.thousand = f1 - right join int4_tbl i2 on i2.f1 = b.tenthous - order by 1; - QUERY PLAN ------------------------------------------------------------------------------------------ - Sort - Sort Key: b.unique1 - -> Nested Loop Left Join - -> Seq Scan on int4_tbl i2 - -> Nested Loop Left Join - Join Filter: (b.unique1 = 42) - -> Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl i1 - -> Index Scan using tenk1_thous_tenthous on tenk1 b - Index Cond: ((thousand = i1.f1) AND (tenthous = i2.f1)) - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = b.unique2) - -> Index Only Scan using tenk1_thous_tenthous on tenk1 c - Index Cond: (thousand = a.thousand) -(15 rows) - -select b.unique1 from - tenk1 a join tenk1 b on a.unique1 = b.unique2 - left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand - join int4_tbl i1 on b.thousand = f1 - right join int4_tbl i2 on i2.f1 = b.tenthous - order by 1; - unique1 ---------- - 0 - - - - -(5 rows) - -explain (costs off) -select * from -( - select unique1, q1, coalesce(unique1, -1) + q1 as fault - from int8_tbl left join tenk1 on (q2 = unique2) -) ss -where fault = 122 -order by fault; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop Left Join - Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122) - -> Seq Scan on int8_tbl - -> Index Scan using tenk1_unique2 on tenk1 - Index Cond: (unique2 = int8_tbl.q2) -(5 rows) - -select * from -( - select unique1, q1, coalesce(unique1, -1) + q1 as fault - from int8_tbl left join tenk1 on (q2 = unique2) -) ss -where fault = 122 -order by fault; - unique1 | q1 | fault ----------+-----+------- - | 123 | 122 -(1 row) - -explain (costs off) -select * from -(values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) -left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x -left join unnest(v1ys) as u1(u1y) on u1y = v2y; - QUERY PLAN -------------------------------------------------------------- - Nested Loop Left Join - -> Values Scan on "*VALUES*" - -> Hash Right Join - Hash Cond: (u1.u1y = "*VALUES*_1".column2) - Filter: ("*VALUES*_1".column1 = "*VALUES*".column1) - -> Function Scan on unnest u1 - -> Hash - -> Values Scan on "*VALUES*_1" -(8 rows) - -select * from -(values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys) -left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x -left join unnest(v1ys) as u1(u1y) on u1y = v2y; - v1x | v1ys | v2x | v2y | u1y ------+---------+-----+-----+----- - 1 | {10,20} | 1 | 10 | 10 - 2 | {20,30} | 2 | 20 | 20 -(2 rows) - --- --- test handling of potential equivalence clauses above outer joins --- -explain (costs off) -select q1, unique2, thousand, hundred - from int8_tbl a left join tenk1 b on q1 = unique2 - where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); - QUERY PLAN ----------------------------------------------------------------------------------------------------------- - Nested Loop Left Join - Filter: ((COALESCE(b.thousand, 123) = COALESCE(b.hundred, 123)) AND (a.q1 = COALESCE(b.hundred, 123))) - -> Seq Scan on int8_tbl a - -> Index Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = a.q1) -(5 rows) - -select q1, unique2, thousand, hundred - from int8_tbl a left join tenk1 b on q1 = unique2 - where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123); - q1 | unique2 | thousand | hundred -----+---------+----------+--------- -(0 rows) - -explain (costs off) -select f1, unique2, case when unique2 is null then f1 else 0 end - from int4_tbl a left join tenk1 b on f1 = unique2 - where (case when unique2 is null then f1 else 0 end) = 0; - QUERY PLAN --------------------------------------------------------------------- - Nested Loop Left Join - Filter: (CASE WHEN (b.unique2 IS NULL) THEN a.f1 ELSE 0 END = 0) - -> Seq Scan on int4_tbl a - -> Index Only Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = a.f1) -(5 rows) - -select f1, unique2, case when unique2 is null then f1 else 0 end - from int4_tbl a left join tenk1 b on f1 = unique2 - where (case when unique2 is null then f1 else 0 end) = 0; - f1 | unique2 | case -----+---------+------ - 0 | 0 | 0 -(1 row) - --- --- another case with equivalence clauses above outer joins (bug #8591) --- -explain (costs off) -select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) - from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand) - where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44; - QUERY PLAN ---------------------------------------------------------------- - Nested Loop Left Join - -> Nested Loop Left Join - Filter: (COALESCE(b.twothousand, a.twothousand) = 44) - -> Index Scan using tenk1_unique2 on tenk1 a - Index Cond: (unique2 < 10) - -> Bitmap Heap Scan on tenk1 b - Recheck Cond: (thousand = a.unique1) - -> Bitmap Index Scan on tenk1_thous_tenthous - Index Cond: (thousand = a.unique1) - -> Index Scan using tenk1_unique2 on tenk1 c - Index Cond: (unique2 = 44) -(11 rows) - -select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand) - from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand) - where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44; - unique1 | unique1 | unique1 | coalesce ----------+---------+---------+---------- -(0 rows) - --- related case -explain (costs off) -select * from int8_tbl t1 left join int8_tbl t2 on t1.q2 = t2.q1, - lateral (select * from int8_tbl t3 where t2.q1 = t2.q2) ss; - QUERY PLAN -------------------------------------------- - Nested Loop - -> Hash Left Join - Hash Cond: (t1.q2 = t2.q1) - Filter: (t2.q1 = t2.q2) - -> Seq Scan on int8_tbl t1 - -> Hash - -> Seq Scan on int8_tbl t2 - -> Seq Scan on int8_tbl t3 -(8 rows) - -select * from int8_tbl t1 left join int8_tbl t2 on t1.q2 = t2.q1, - lateral (select * from int8_tbl t3 where t2.q1 = t2.q2) ss; - q1 | q2 | q1 | q2 | q1 | q2 -------------------+------------------+------------------+------------------+------------------+------------------- - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 456 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 456 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 -(10 rows) - --- --- check handling of join aliases when flattening multiple levels of subquery --- -explain (verbose, costs off) -select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from - (values (0),(1)) foo1(join_key) -left join - (select join_key, bug_field from - (select ss1.join_key, ss1.bug_field from - (select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1 - ) foo2 - left join - (select unique2 as join_key from tenk1 i2) ss2 - using (join_key) - ) foo3 -using (join_key); - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop Left Join - Output: "*VALUES*".column1, i1.f1, (666) - Join Filter: ("*VALUES*".column1 = i1.f1) - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> Materialize - Output: i1.f1, (666) - -> Nested Loop Left Join - Output: i1.f1, 666 - -> Seq Scan on public.int4_tbl i1 - Output: i1.f1 - -> Index Only Scan using tenk1_unique2 on public.tenk1 i2 - Output: i2.unique2 - Index Cond: (i2.unique2 = i1.f1) -(14 rows) - -select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from - (values (0),(1)) foo1(join_key) -left join - (select join_key, bug_field from - (select ss1.join_key, ss1.bug_field from - (select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1 - ) foo2 - left join - (select unique2 as join_key from tenk1 i2) ss2 - using (join_key) - ) foo3 -using (join_key); - foo1_id | foo3_id | bug_field ----------+---------+----------- - 0 | 0 | 666 - 1 | | -(2 rows) - --- --- check handling of a variable-free join alias --- -explain (verbose, costs off) -select * from -int4_tbl i0 left join -( (select *, 123 as x from int4_tbl i1) ss1 - left join - (select *, q2 as x from int8_tbl i2) ss2 - using (x) -) ss0 -on (i0.f1 = ss0.f1) -order by i0.f1, x; - QUERY PLAN -------------------------------------------------------------- - Sort - Output: i0.f1, ('123'::bigint), i1.f1, i2.q1, i2.q2 - Sort Key: i0.f1, ('123'::bigint) - -> Hash Right Join - Output: i0.f1, ('123'::bigint), i1.f1, i2.q1, i2.q2 - Hash Cond: (i1.f1 = i0.f1) - -> Nested Loop Left Join - Output: i1.f1, i2.q1, i2.q2, '123'::bigint - -> Seq Scan on public.int4_tbl i1 - Output: i1.f1 - -> Materialize - Output: i2.q1, i2.q2 - -> Seq Scan on public.int8_tbl i2 - Output: i2.q1, i2.q2 - Filter: (123 = i2.q2) - -> Hash - Output: i0.f1 - -> Seq Scan on public.int4_tbl i0 - Output: i0.f1 -(19 rows) - -select * from -int4_tbl i0 left join -( (select *, 123 as x from int4_tbl i1) ss1 - left join - (select *, q2 as x from int8_tbl i2) ss2 - using (x) -) ss0 -on (i0.f1 = ss0.f1) -order by i0.f1, x; - f1 | x | f1 | q1 | q2 --------------+-----+-------------+------------------+----- - -2147483647 | 123 | -2147483647 | 4567890123456789 | 123 - -123456 | 123 | -123456 | 4567890123456789 | 123 - 0 | 123 | 0 | 4567890123456789 | 123 - 123456 | 123 | 123456 | 4567890123456789 | 123 - 2147483647 | 123 | 2147483647 | 4567890123456789 | 123 -(5 rows) - --- --- test successful handling of nested outer joins with degenerate join quals --- -explain (verbose, costs off) -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - QUERY PLAN ----------------------------------------------------------------------- - Hash Left Join - Output: t1.f1 - Hash Cond: (i8.q2 = i4.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q2 - Join Filter: (t1.f1 = '***'::text) - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q2 - -> Hash Right Join - Output: i8.q2 - Hash Cond: ((NULL::integer) = i8b1.q2) - -> Hash Join - Output: i8.q2, (NULL::integer) - Hash Cond: (i8.q1 = i8b2.q1) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Hash - Output: i8b2.q1, (NULL::integer) - -> Seq Scan on public.int8_tbl i8b2 - Output: i8b2.q1, NULL::integer - -> Hash - Output: i8b1.q2 - -> Seq Scan on public.int8_tbl i8b1 - Output: i8b1.q2 - -> Hash - Output: i4.f1 - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 -(30 rows) - -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - f1 -------------------- - doh! - hi de ho neighbor -(2 rows) - -explain (verbose, costs off) -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - QUERY PLAN ----------------------------------------------------------------------------- - Hash Left Join - Output: t1.f1 - Hash Cond: (i8.q2 = i4.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q2 - Join Filter: (t1.f1 = '***'::text) - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q2 - -> Hash Right Join - Output: i8.q2 - Hash Cond: ((NULL::integer) = i8b1.q2) - -> Hash Right Join - Output: i8.q2, (NULL::integer) - Hash Cond: (i8b2.q1 = i8.q1) - -> Nested Loop - Output: i8b2.q1, NULL::integer - -> Seq Scan on public.int8_tbl i8b2 - Output: i8b2.q1, i8b2.q2 - -> Materialize - -> Seq Scan on public.int4_tbl i4b2 - -> Hash - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Hash - Output: i8b1.q2 - -> Seq Scan on public.int8_tbl i8b1 - Output: i8b1.q2 - -> Hash - Output: i4.f1 - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 -(34 rows) - -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - f1 -------------------- - doh! - hi de ho neighbor -(2 rows) - -explain (verbose, costs off) -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2 - where q1 = f1) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - QUERY PLAN ----------------------------------------------------------------------------- - Hash Left Join - Output: t1.f1 - Hash Cond: (i8.q2 = i4.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q2 - Join Filter: (t1.f1 = '***'::text) - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q2 - -> Hash Right Join - Output: i8.q2 - Hash Cond: ((NULL::integer) = i8b1.q2) - -> Hash Right Join - Output: i8.q2, (NULL::integer) - Hash Cond: (i8b2.q1 = i8.q1) - -> Hash Join - Output: i8b2.q1, NULL::integer - Hash Cond: (i8b2.q1 = i4b2.f1) - -> Seq Scan on public.int8_tbl i8b2 - Output: i8b2.q1, i8b2.q2 - -> Hash - Output: i4b2.f1 - -> Seq Scan on public.int4_tbl i4b2 - Output: i4b2.f1 - -> Hash - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Hash - Output: i8b1.q2 - -> Seq Scan on public.int8_tbl i8b1 - Output: i8b1.q2 - -> Hash - Output: i4.f1 - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 -(37 rows) - -select t1.* from - text_tbl t1 - left join (select *, '***'::text as d1 from int8_tbl i8b1) b1 - left join int8_tbl i8 - left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2 - where q1 = f1) b2 - on (i8.q1 = b2.q1) - on (b2.d2 = b1.q2) - on (t1.f1 = b1.d1) - left join int4_tbl i4 - on (i8.q2 = i4.f1); - f1 -------------------- - doh! - hi de ho neighbor -(2 rows) - -explain (verbose, costs off) -select * from - text_tbl t1 - inner join int8_tbl i8 - on i8.q2 = 456 - right join text_tbl t2 - on t1.f1 = 'doh!' - left join int4_tbl i4 - on i8.q1 = i4.f1; - QUERY PLAN --------------------------------------------------------- - Nested Loop Left Join - Output: t1.f1, i8.q1, i8.q2, t2.f1, i4.f1 - -> Seq Scan on public.text_tbl t2 - Output: t2.f1 - -> Materialize - Output: i8.q1, i8.q2, i4.f1, t1.f1 - -> Nested Loop - Output: i8.q1, i8.q2, i4.f1, t1.f1 - -> Nested Loop Left Join - Output: i8.q1, i8.q2, i4.f1 - Join Filter: (i8.q1 = i4.f1) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 456) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - Filter: (t1.f1 = 'doh!'::text) -(19 rows) - -select * from - text_tbl t1 - inner join int8_tbl i8 - on i8.q2 = 456 - right join text_tbl t2 - on t1.f1 = 'doh!' - left join int4_tbl i4 - on i8.q1 = i4.f1; - f1 | q1 | q2 | f1 | f1 -------+-----+-----+-------------------+---- - doh! | 123 | 456 | doh! | - doh! | 123 | 456 | hi de ho neighbor | -(2 rows) - --- check handling of a variable-free qual for a non-commutable outer join -explain (costs off) -select nspname -from (select 1 as x) ss1 -left join -( select n.nspname, c.relname - from pg_class c left join pg_namespace n on n.oid = c.relnamespace - where c.relkind = 'r' -) ss2 on false; - QUERY PLAN --------------------------------- - Nested Loop Left Join - Join Filter: false - -> Result - -> Result - Replaces: Join on c, n - One-Time Filter: false -(6 rows) - --- check handling of apparently-commutable outer joins with non-commutable --- joins between them -explain (costs off) -select 1 from - int4_tbl i4 - left join int8_tbl i8 on i4.f1 is not null - left join (select 1 as a) ss1 on null - join int4_tbl i42 on ss1.a is null or i8.q1 <> i8.q2 - right join (select 2 as b) ss2 - on ss2.b < i4.f1; - QUERY PLAN ------------------------------------------------------------ - Nested Loop Left Join - -> Result - -> Nested Loop - -> Nested Loop Left Join - Join Filter: NULL::boolean - Filter: (((1) IS NULL) OR (i8.q1 <> i8.q2)) - -> Nested Loop Left Join - Join Filter: (i4.f1 IS NOT NULL) - -> Seq Scan on int4_tbl i4 - Filter: (2 < f1) - -> Materialize - -> Seq Scan on int8_tbl i8 - -> Result - One-Time Filter: false - -> Materialize - -> Seq Scan on int4_tbl i42 -(16 rows) - --- --- test for appropriate join order in the presence of lateral references --- -explain (verbose, costs off) -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss -where t1.f1 = ss.f1; - QUERY PLAN --------------------------------------------------- - Nested Loop - Output: t1.f1, i8.q1, i8.q2, (i8.q1), t2.f1 - Join Filter: (t1.f1 = t2.f1) - -> Nested Loop Left Join - Output: t1.f1, i8.q1, i8.q2 - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 123) - -> Memoize - Output: (i8.q1), t2.f1 - Cache Key: i8.q1 - Cache Mode: binary - -> Limit - Output: (i8.q1), t2.f1 - -> Seq Scan on public.text_tbl t2 - Output: i8.q1, t2.f1 -(20 rows) - -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss -where t1.f1 = ss.f1; - f1 | q1 | q2 | q1 | f1 -------+------------------+-----+------------------+------ - doh! | 4567890123456789 | 123 | 4567890123456789 | doh! -(1 row) - -explain (verbose, costs off) -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1, - lateral (select ss1.* from text_tbl t3 limit 1) as ss2 -where t1.f1 = ss2.f1; - QUERY PLAN -------------------------------------------------------------------- - Nested Loop - Output: t1.f1, i8.q1, i8.q2, (i8.q1), t2.f1, ((i8.q1)), (t2.f1) - Join Filter: (t1.f1 = (t2.f1)) - -> Nested Loop - Output: t1.f1, i8.q1, i8.q2, (i8.q1), t2.f1 - -> Nested Loop Left Join - Output: t1.f1, i8.q1, i8.q2 - -> Seq Scan on public.text_tbl t1 - Output: t1.f1 - -> Materialize - Output: i8.q1, i8.q2 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 123) - -> Memoize - Output: (i8.q1), t2.f1 - Cache Key: i8.q1 - Cache Mode: binary - -> Limit - Output: (i8.q1), t2.f1 - -> Seq Scan on public.text_tbl t2 - Output: i8.q1, t2.f1 - -> Memoize - Output: ((i8.q1)), (t2.f1) - Cache Key: (i8.q1), t2.f1 - Cache Mode: binary - -> Limit - Output: ((i8.q1)), (t2.f1) - -> Seq Scan on public.text_tbl t3 - Output: (i8.q1), t2.f1 -(30 rows) - -select * from - text_tbl t1 - left join int8_tbl i8 - on i8.q2 = 123, - lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1, - lateral (select ss1.* from text_tbl t3 limit 1) as ss2 -where t1.f1 = ss2.f1; - f1 | q1 | q2 | q1 | f1 | q1 | f1 -------+------------------+-----+------------------+------+------------------+------ - doh! | 4567890123456789 | 123 | 4567890123456789 | doh! | 4567890123456789 | doh! -(1 row) - -explain (verbose, costs off) -select 1 from - text_tbl as tt1 - inner join text_tbl as tt2 on (tt1.f1 = 'foo') - left join text_tbl as tt3 on (tt3.f1 = 'foo') - left join text_tbl as tt4 on (tt3.f1 = tt4.f1), - lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1 -where tt1.f1 = ss1.c0; - QUERY PLAN ----------------------------------------------------------- - Nested Loop - Output: 1 - -> Nested Loop Left Join - Output: tt1.f1, tt4.f1 - -> Nested Loop - Output: tt1.f1 - -> Seq Scan on public.text_tbl tt1 - Output: tt1.f1 - Filter: (tt1.f1 = 'foo'::text) - -> Seq Scan on public.text_tbl tt2 - Output: tt2.f1 - -> Materialize - Output: tt4.f1 - -> Nested Loop Left Join - Output: tt4.f1 - -> Seq Scan on public.text_tbl tt3 - Output: tt3.f1 - Filter: (tt3.f1 = 'foo'::text) - -> Seq Scan on public.text_tbl tt4 - Output: tt4.f1 - Filter: (tt4.f1 = 'foo'::text) - -> Memoize - Output: ss1.c0 - Cache Key: tt4.f1 - Cache Mode: binary - -> Subquery Scan on ss1 - Output: ss1.c0 - Filter: (ss1.c0 = 'foo'::text) - -> Limit - Output: (tt4.f1) - -> Seq Scan on public.text_tbl tt5 - Output: tt4.f1 -(32 rows) - -select 1 from - text_tbl as tt1 - inner join text_tbl as tt2 on (tt1.f1 = 'foo') - left join text_tbl as tt3 on (tt3.f1 = 'foo') - left join text_tbl as tt4 on (tt3.f1 = tt4.f1), - lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1 -where tt1.f1 = ss1.c0; - ?column? ----------- -(0 rows) - -explain (verbose, costs off) -select 1 from - int4_tbl as i4 - inner join - ((select 42 as n from int4_tbl x1 left join int8_tbl x2 on f1 = q1) as ss1 - right join (select 1 as z) as ss2 on true) - on false, - lateral (select i4.f1, ss1.n from int8_tbl as i8 limit 1) as ss3; - QUERY PLAN ------------------------------------------------ - Result - Output: 1 - Replaces: Join on i4, ss3, x1, x2, *RESULT* - One-Time Filter: false -(4 rows) - -select 1 from - int4_tbl as i4 - inner join - ((select 42 as n from int4_tbl x1 left join int8_tbl x2 on f1 = q1) as ss1 - right join (select 1 as z) as ss2 on true) - on false, - lateral (select i4.f1, ss1.n from int8_tbl as i8 limit 1) as ss3; - ?column? ----------- -(0 rows) - --- --- check a case where we formerly generated invalid parameterized paths --- -begin; -create temp table t (a int unique); -explain (costs off) -select 1 from t t1 - join lateral (select t1.a from (select 1) foo offset 0) as s1 on true - join - (select 1 from t t2 - inner join (t t3 - left join (t t4 left join t t5 on t4.a = 1) - on t3.a = t4.a) - on false - where t3.a = coalesce(t5.a,1)) as s2 - on true; - QUERY PLAN --------------------------------------------- - Result - Replaces: Join on t1, s1, t2, t3, t4, t5 - One-Time Filter: false -(3 rows) - -rollback; --- --- check a case in which a PlaceHolderVar forces join order --- -explain (verbose, costs off) -select ss2.* from - int4_tbl i41 - left join int8_tbl i8 - join (select i42.f1 as c1, i43.f1 as c2, 42 as c3 - from int4_tbl i42, int4_tbl i43) ss1 - on i8.q1 = ss1.c2 - on i41.f1 = ss1.c1, - lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2 -where ss1.c2 = 0; - QUERY PLAN ------------------------------------------------------------------------- - Nested Loop - Output: (i41.f1), (i8.q1), (i8.q2), (i42.f1), (i43.f1), ((42)) - -> Hash Join - Output: i41.f1, i42.f1, i8.q1, i8.q2, i43.f1, 42 - Hash Cond: (i41.f1 = i42.f1) - -> Nested Loop - Output: i8.q1, i8.q2, i43.f1, i41.f1 - -> Nested Loop - Output: i8.q1, i8.q2, i43.f1 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q1 = 0) - -> Seq Scan on public.int4_tbl i43 - Output: i43.f1 - Filter: (i43.f1 = 0) - -> Seq Scan on public.int4_tbl i41 - Output: i41.f1 - -> Hash - Output: i42.f1 - -> Seq Scan on public.int4_tbl i42 - Output: i42.f1 - -> Limit - Output: (i41.f1), (i8.q1), (i8.q2), (i42.f1), (i43.f1), ((42)) - -> Seq Scan on public.text_tbl - Output: i41.f1, i8.q1, i8.q2, i42.f1, i43.f1, (42) -(25 rows) - -select ss2.* from - int4_tbl i41 - left join int8_tbl i8 - join (select i42.f1 as c1, i43.f1 as c2, 42 as c3 - from int4_tbl i42, int4_tbl i43) ss1 - on i8.q1 = ss1.c2 - on i41.f1 = ss1.c1, - lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2 -where ss1.c2 = 0; - f1 | q1 | q2 | c1 | c2 | c3 -----+----+----+----+----+---- -(0 rows) - --- --- test successful handling of full join underneath left join (bug #14105) --- -explain (costs off) -select * from - (select 1 as id) as xx - left join - (tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id)) - on (xx.id = coalesce(yy.id, yy.id)); - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - -> Result - -> Hash Full Join - Hash Cond: (a1.unique1 = (1)) - Filter: (1 = COALESCE((1), (1))) - -> Seq Scan on tenk1 a1 - -> Hash - -> Result -(8 rows) - -select * from - (select 1 as id) as xx - left join - (tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id)) - on (xx.id = coalesce(yy.id, yy.id)); - id | unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 | id -----+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------+---- - 1 | 1 | 2838 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 3 | BAAAAA | EFEAAA | OOOOxx | 1 -(1 row) - --- --- test ability to push constants through outer join clauses --- -explain (costs off) - select * from int4_tbl a left join tenk1 b on f1 = unique2 where f1 = 0; - QUERY PLAN -------------------------------------------------- - Nested Loop Left Join - -> Seq Scan on int4_tbl a - Filter: (f1 = 0) - -> Index Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = 0) -(5 rows) - -explain (costs off) - select * from tenk1 a full join tenk1 b using(unique2) where unique2 = 42; - QUERY PLAN -------------------------------------------------- - Merge Full Join - -> Index Scan using tenk1_unique2 on tenk1 a - Index Cond: (unique2 = 42) - -> Index Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = 42) -(5 rows) - --- --- test that quals attached to an outer join have correct semantics, --- specifically that they don't re-use expressions computed below the join; --- we force a mergejoin so that coalesce(b.q1, 1) appears as a join input --- -set enable_hashjoin to off; -set enable_nestloop to off; -explain (verbose, costs off) - select a.q2, b.q1 - from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1) - where coalesce(b.q1, 1) > 0; - QUERY PLAN ---------------------------------------------------------- - Merge Left Join - Output: a.q2, b.q1 - Merge Cond: (a.q2 = (COALESCE(b.q1, '1'::bigint))) - Filter: (COALESCE(b.q1, '1'::bigint) > 0) - -> Sort - Output: a.q2 - Sort Key: a.q2 - -> Seq Scan on public.int8_tbl a - Output: a.q2 - -> Sort - Output: b.q1, (COALESCE(b.q1, '1'::bigint)) - Sort Key: (COALESCE(b.q1, '1'::bigint)) - -> Seq Scan on public.int8_tbl b - Output: b.q1, COALESCE(b.q1, '1'::bigint) -(14 rows) - -select a.q2, b.q1 - from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1) - where coalesce(b.q1, 1) > 0; - q2 | q1 --------------------+------------------ - -4567890123456789 | - 123 | 123 - 123 | 123 - 456 | - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 -(10 rows) - -reset enable_hashjoin; -reset enable_nestloop; --- --- test join strength reduction with a SubPlan providing the proof --- -explain (costs off) -select a.unique1, b.unique2 - from onek a left join onek b on a.unique1 = b.unique2 - where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- - Hash Join - Hash Cond: (b.unique2 = a.unique1) - -> Seq Scan on onek b - Filter: (ANY ((unique2 = (SubPlan any_1).col1) AND ((random() > '0'::double precision) = (SubPlan any_1).col2))) - SubPlan any_1 - -> Seq Scan on int8_tbl c - Filter: (q1 < b.unique1) - -> Hash - -> Index Only Scan using onek_unique1 on onek a -(9 rows) - -select a.unique1, b.unique2 - from onek a left join onek b on a.unique1 = b.unique2 - where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1); - unique1 | unique2 ----------+--------- - 123 | 123 -(1 row) - --- --- test full-join strength reduction --- -explain (costs off) -select a.unique1, b.unique2 - from onek a full join onek b on a.unique1 = b.unique2 - where a.unique1 = 42; - QUERY PLAN ----------------------------------------------------- - Nested Loop Left Join - -> Index Only Scan using onek_unique1 on onek a - Index Cond: (unique1 = 42) - -> Index Only Scan using onek_unique2 on onek b - Index Cond: (unique2 = 42) -(5 rows) - -select a.unique1, b.unique2 - from onek a full join onek b on a.unique1 = b.unique2 - where a.unique1 = 42; - unique1 | unique2 ----------+--------- - 42 | 42 -(1 row) - -explain (costs off) -select a.unique1, b.unique2 - from onek a full join onek b on a.unique1 = b.unique2 - where b.unique2 = 43; - QUERY PLAN ----------------------------------------------------- - Nested Loop Left Join - -> Index Only Scan using onek_unique2 on onek b - Index Cond: (unique2 = 43) - -> Index Only Scan using onek_unique1 on onek a - Index Cond: (unique1 = 43) -(5 rows) - -select a.unique1, b.unique2 - from onek a full join onek b on a.unique1 = b.unique2 - where b.unique2 = 43; - unique1 | unique2 ----------+--------- - 43 | 43 -(1 row) - -explain (costs off) -select a.unique1, b.unique2 - from onek a full join onek b on a.unique1 = b.unique2 - where a.unique1 = 42 and b.unique2 = 42; - QUERY PLAN ----------------------------------------------------- - Nested Loop - -> Index Only Scan using onek_unique1 on onek a - Index Cond: (unique1 = 42) - -> Index Only Scan using onek_unique2 on onek b - Index Cond: (unique2 = 42) -(5 rows) - -select a.unique1, b.unique2 - from onek a full join onek b on a.unique1 = b.unique2 - where a.unique1 = 42 and b.unique2 = 42; - unique1 | unique2 ----------+--------- - 42 | 42 -(1 row) - --- --- test result-RTE removal underneath a full join --- -explain (costs off) -select * from - (select * from int8_tbl i81 join (values(123,2)) v(v1,v2) on q2=v1) ss1 -full join - (select * from (values(456,2)) w(v1,v2) join int8_tbl i82 on q2=v1) ss2 -on true; - QUERY PLAN --------------------------------------- - Merge Full Join - -> Seq Scan on int8_tbl i81 - Filter: (q2 = 123) - -> Materialize - -> Seq Scan on int8_tbl i82 - Filter: (q2 = 456) -(6 rows) - -select * from - (select * from int8_tbl i81 join (values(123,2)) v(v1,v2) on q2=v1) ss1 -full join - (select * from (values(456,2)) w(v1,v2) join int8_tbl i82 on q2=v1) ss2 -on true; - q1 | q2 | v1 | v2 | v1 | v2 | q1 | q2 -------------------+-----+-----+----+-----+----+-----+----- - 4567890123456789 | 123 | 123 | 2 | 456 | 2 | 123 | 456 -(1 row) - --- --- test join removal --- -begin; -CREATE TEMP TABLE a (id int PRIMARY KEY, b_id int); -CREATE TEMP TABLE b (id int PRIMARY KEY, c_id int); -CREATE TEMP TABLE c (id int PRIMARY KEY); -CREATE TEMP TABLE d (a int, b int); -CREATE TEMP TABLE e (id1 int, id2 int, PRIMARY KEY(id1, id2)); -INSERT INTO a VALUES (0, 0), (1, NULL); -INSERT INTO b VALUES (0, 0), (1, NULL); -INSERT INTO c VALUES (0), (1); -INSERT INTO d VALUES (1,3), (2,2), (3,1); -INSERT INTO e VALUES (0,0), (2,2), (3,1); --- all these cases should be optimizable into a simple seqscan -explain (costs off) SELECT a.* FROM a LEFT JOIN b ON a.b_id = b.id; - QUERY PLAN ---------------- - Seq Scan on a -(1 row) - -explain (costs off) SELECT b.* FROM b LEFT JOIN c ON b.c_id = c.id; - QUERY PLAN ---------------- - Seq Scan on b -(1 row) - -explain (costs off) - SELECT a.* FROM a LEFT JOIN (b left join c on b.c_id = c.id) - ON (a.b_id = b.id); - QUERY PLAN ---------------- - Seq Scan on a -(1 row) - -explain (costs off) - SELECT a.* FROM a LEFT JOIN b ON a.id = b.id - LEFT JOIN e ON e.id1 = a.b_id AND b.c_id = e.id2; - QUERY PLAN ---------------- - Seq Scan on a -(1 row) - --- check optimization of outer join within another special join -explain (costs off) -select id from a where id in ( - select b.id from b left join c on b.id = c.id -); - QUERY PLAN ----------------------------- - Hash Join - Hash Cond: (a.id = b.id) - -> Seq Scan on a - -> Hash - -> Seq Scan on b -(5 rows) - --- check optimization with oddly-nested outer joins -explain (costs off) -select a1.id from - (a a1 left join a a2 on true) - left join - (a a3 left join a a4 on a3.id = a4.id) - on a2.id = a3.id; - QUERY PLAN ------------------------------- - Nested Loop Left Join - -> Seq Scan on a a1 - -> Materialize - -> Seq Scan on a a2 -(4 rows) - -explain (costs off) -select a1.id from - (a a1 left join a a2 on a1.id = a2.id) - left join - (a a3 left join a a4 on a3.id = a4.id) - on a2.id = a3.id; - QUERY PLAN ------------------- - Seq Scan on a a1 -(1 row) - -explain (costs off) -select 1 from a t1 - left join a t2 on true - inner join a t3 on true - left join a t4 on t2.id = t4.id and t2.id = t3.id; - QUERY PLAN ------------------------------------- - Nested Loop - -> Nested Loop Left Join - -> Seq Scan on a t1 - -> Materialize - -> Seq Scan on a t2 - -> Materialize - -> Seq Scan on a t3 -(7 rows) - --- another example (bug #17781) -explain (costs off) -select ss1.f1 -from int4_tbl as t1 - left join (int4_tbl as t2 - right join int4_tbl as t3 on null - left join (int4_tbl as t4 - right join int8_tbl as t5 on null) - on t2.f1 = t4.f1 - left join ((select null as f1 from int4_tbl as t6) as ss1 - inner join int8_tbl as t7 on null) - on t5.q1 = t7.q2) - on false; - QUERY PLAN --------------------------------------------------- - Nested Loop Left Join - Join Filter: false - -> Seq Scan on int4_tbl t1 - -> Result - Replaces: Join on t2, t3, t4, t5, t7, t6 - One-Time Filter: false -(6 rows) - --- variant with Var rather than PHV coming from t6 -explain (costs off) -select ss1.f1 -from int4_tbl as t1 - left join (int4_tbl as t2 - right join int4_tbl as t3 on null - left join (int4_tbl as t4 - right join int8_tbl as t5 on null) - on t2.f1 = t4.f1 - left join ((select f1 from int4_tbl as t6) as ss1 - inner join int8_tbl as t7 on null) - on t5.q1 = t7.q2) - on false; - QUERY PLAN --------------------------------------------------- - Nested Loop Left Join - Join Filter: false - -> Seq Scan on int4_tbl t1 - -> Result - Replaces: Join on t2, t3, t4, t5, t7, t6 - One-Time Filter: false -(6 rows) - --- per further discussion of bug #17781 -explain (costs off) -select ss1.x -from (select f1/2 as x from int4_tbl i4 left join a on a.id = i4.f1) ss1 - right join int8_tbl i8 on true -where current_user is not null; -- this is to add a Result node - QUERY PLAN ------------------------------------------------ - Result - One-Time Filter: (CURRENT_USER IS NOT NULL) - -> Nested Loop Left Join - -> Seq Scan on int8_tbl i8 - -> Materialize - -> Seq Scan on int4_tbl i4 -(6 rows) - --- and further discussion of bug #17781 -explain (costs off) -select * -from int8_tbl t1 - left join (int8_tbl t2 left join onek t3 on t2.q1 > t3.unique1) - on t1.q2 = t2.q2 - left join onek t4 - on t2.q2 < t3.unique2; - QUERY PLAN -------------------------------------------------- - Nested Loop Left Join - Join Filter: (t2.q2 < t3.unique2) - -> Nested Loop Left Join - Join Filter: (t2.q1 > t3.unique1) - -> Hash Left Join - Hash Cond: (t1.q2 = t2.q2) - -> Seq Scan on int8_tbl t1 - -> Hash - -> Seq Scan on int8_tbl t2 - -> Materialize - -> Seq Scan on onek t3 - -> Materialize - -> Seq Scan on onek t4 -(13 rows) - --- More tests of correct placement of pseudoconstant quals --- simple constant-false condition -explain (costs off) -select * from int8_tbl t1 left join - (int8_tbl t2 inner join int8_tbl t3 on false - left join int8_tbl t4 on t2.q2 = t4.q2) -on t1.q1 = t2.q1; - QUERY PLAN --------------------------------------------- - Hash Left Join - Hash Cond: (t1.q1 = t2.q1) - -> Seq Scan on int8_tbl t1 - -> Hash - -> Result - Replaces: Join on t2, t3, t4 - One-Time Filter: false -(7 rows) - --- deduce constant-false from an EquivalenceClass -explain (costs off) -select * from int8_tbl t1 left join - (int8_tbl t2 inner join int8_tbl t3 on (t2.q1-t3.q2) = 0 and (t2.q1-t3.q2) = 1 - left join int8_tbl t4 on t2.q2 = t4.q2) -on t1.q1 = t2.q1; - QUERY PLAN --------------------------------------------- - Hash Left Join - Hash Cond: (t1.q1 = t2.q1) - -> Seq Scan on int8_tbl t1 - -> Hash - -> Result - Replaces: Join on t2, t3, t4 - One-Time Filter: false -(7 rows) - --- pseudoconstant based on an outer-level Param -explain (costs off) -select exists( - select * from int8_tbl t1 left join - (int8_tbl t2 inner join int8_tbl t3 on x0.f1 = 1 - left join int8_tbl t4 on t2.q2 = t4.q2) - on t1.q1 = t2.q1 -) from int4_tbl x0; - QUERY PLAN ---------------------------------------------------------------------- - Seq Scan on int4_tbl x0 - SubPlan exists_1 - -> Nested Loop Left Join - Join Filter: (t2.q2 = t4.q2) - -> Nested Loop Left Join - Join Filter: (t1.q1 = t2.q1) - -> Seq Scan on int8_tbl t1 - -> Materialize - -> Result - One-Time Filter: (x0.f1 = 1) - -> Nested Loop - -> Seq Scan on int8_tbl t2 - -> Materialize - -> Seq Scan on int8_tbl t3 - -> Materialize - -> Seq Scan on int8_tbl t4 -(16 rows) - --- check that join removal works for a left join when joining a subquery --- that is guaranteed to be unique by its GROUP BY clause -explain (costs off) -select d.* from d left join (select * from b group by b.id, b.c_id) s - on d.a = s.id and d.b = s.c_id; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- check that join removal works for a left join when joining a subquery --- that is guaranteed to be unique by GROUPING SETS -explain (costs off) -select d.* from d left join (select 1 as x from b group by ()) s - on d.a = s.x; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - -explain (costs off) -select d.* from d left join (select 1 as x from b group by grouping sets(())) s - on d.a = s.x; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - -explain (costs off) -select d.* from d left join (select 1 as x from b group by grouping sets(()), grouping sets(())) s - on d.a = s.x; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - -explain (costs off) -select d.* from d left join (select 1 as x from b group by distinct grouping sets((), ())) s - on d.a = s.x; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- similarly, but keying off a DISTINCT clause -explain (costs off) -select d.* from d left join (select distinct * from b) s - on d.a = s.id and d.b = s.c_id; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- join removal is not possible when the GROUP BY contains a column that is --- not in the join condition. (Note: as of 9.6, we notice that b.id is a --- primary key and so drop b.c_id from the GROUP BY of the resulting plan; --- but this happens too late for join removal in the outer plan level.) -explain (costs off) -select d.* from d left join (select * from b group by b.id, b.c_id) s - on d.a = s.id; - QUERY PLAN ------------------------------------------- - Merge Right Join - Merge Cond: (b.id = d.a) - -> Group - Group Key: b.id - -> Index Scan using b_pkey on b - -> Sort - Sort Key: d.a - -> Seq Scan on d -(8 rows) - --- join removal is not possible when the GROUP BY contains non-empty grouping --- sets or multiple empty grouping sets -explain (costs off) -select d.* from d left join (select 1 as x from b group by rollup(x)) s - on d.a = s.x; - QUERY PLAN ---------------------------------- - Hash Left Join - Hash Cond: (d.a = (1)) - -> Seq Scan on d - -> Hash - -> MixedAggregate - Hash Key: 1 - Group Key: () - -> Seq Scan on b -(8 rows) - -explain (costs off) -select d.* from d left join (select 1 as x from b group by grouping sets((), ())) s - on d.a = s.x; - QUERY PLAN ------------------------------------------ - Hash Left Join - Hash Cond: (d.a = (1)) - -> Seq Scan on d - -> Hash - -> Append - -> Result - Replaces: Aggregate - -> Result - Replaces: Aggregate -(9 rows) - -explain (costs off) -select d.* from d left join (select 1 as x from b group by grouping sets((), grouping sets(()))) s - on d.a = s.x; - QUERY PLAN ------------------------------------------ - Hash Left Join - Hash Cond: (d.a = (1)) - -> Seq Scan on d - -> Hash - -> Append - -> Result - Replaces: Aggregate - -> Result - Replaces: Aggregate -(9 rows) - --- similarly, but keying off a DISTINCT clause -explain (costs off) -select d.* from d left join (select distinct * from b) s - on d.a = s.id; - QUERY PLAN --------------------------------------- - Merge Right Join - Merge Cond: (b.id = d.a) - -> Unique - -> Sort - Sort Key: b.id, b.c_id - -> Seq Scan on b - -> Sort - Sort Key: d.a - -> Seq Scan on d -(9 rows) - --- join removal is not possible here -explain (costs off) -select 1 from a t1 - left join (a t2 left join a t3 on t2.id = 1) on t2.id = 1; - QUERY PLAN --------------------------------------------------------- - Nested Loop Left Join - -> Seq Scan on a t1 - -> Materialize - -> Nested Loop Left Join - Join Filter: (t2.id = 1) - -> Index Only Scan using a_pkey on a t2 - Index Cond: (id = 1) - -> Seq Scan on a t3 -(8 rows) - --- check join removal works when uniqueness of the join condition is enforced --- by a UNION -explain (costs off) -select d.* from d left join (select id from a union select id from b) s - on d.a = s.id; - QUERY PLAN ---------------- - Seq Scan on d -(1 row) - --- check join removal with a cross-type comparison operator -explain (costs off) -select i8.* from int8_tbl i8 left join (select f1 from int4_tbl group by f1) i4 - on i8.q1 = i4.f1; - QUERY PLAN -------------------------- - Seq Scan on int8_tbl i8 -(1 row) - --- check join removal with lateral references -explain (costs off) -select 1 from (select a.id FROM a left join b on a.b_id = b.id) q, - lateral generate_series(1, q.id) gs(i) where q.id = gs.i; - QUERY PLAN -------------------------------------------- - Nested Loop - -> Seq Scan on a - -> Function Scan on generate_series gs - Filter: (a.id = i) -(4 rows) - --- check join removal within RHS of an outer join -explain (costs off) -select c.id, ss.a from c - left join (select d.a from onerow, d left join b on d.a = b.id) ss - on c.id = ss.a; - QUERY PLAN --------------------------------- - Hash Right Join - Hash Cond: (d.a = c.id) - -> Nested Loop - -> Seq Scan on onerow - -> Seq Scan on d - -> Hash - -> Seq Scan on c -(7 rows) - --- check the case when the placeholder relates to an outer join and its --- inner in the press field but actually uses only the outer side of the join -explain (costs off) -SELECT q.val FROM b LEFT JOIN ( - SELECT (q1.z IS NOT NULL) AS val - FROM b LEFT JOIN ( - SELECT (t1.b_id IS NOT NULL) AS z FROM a t1 LEFT JOIN a t2 USING (id) - ) AS q1 - ON true -) AS q ON true; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - -> Seq Scan on b - -> Materialize - -> Nested Loop Left Join - -> Seq Scan on b b_1 - -> Materialize - -> Seq Scan on a t1 -(7 rows) - -CREATE TEMP TABLE parted_b (id int PRIMARY KEY) partition by range(id); -CREATE TEMP TABLE parted_b1 partition of parted_b for values from (0) to (10); --- test join removals on a partitioned table -explain (costs off) -select a.* from a left join parted_b pb on a.b_id = pb.id; - QUERY PLAN ---------------- - Seq Scan on a -(1 row) - -rollback; -create temp table parent (k int primary key, pd int); -create temp table child (k int unique, cd int); -insert into parent values (1, 10), (2, 20), (3, 30); -insert into child values (1, 100), (4, 400); --- this case is optimizable -select p.* from parent p left join child c on (p.k = c.k); - k | pd ----+---- - 1 | 10 - 2 | 20 - 3 | 30 -(3 rows) - -explain (costs off) - select p.* from parent p left join child c on (p.k = c.k); - QUERY PLAN ----------------------- - Seq Scan on parent p -(1 row) - --- this case is not -select p.*, linked from parent p - left join (select c.*, true as linked from child c) as ss - on (p.k = ss.k); - k | pd | linked ----+----+-------- - 1 | 10 | t - 2 | 20 | - 3 | 30 | -(3 rows) - -explain (costs off) - select p.*, linked from parent p - left join (select c.*, true as linked from child c) as ss - on (p.k = ss.k); - QUERY PLAN ---------------------------------- - Hash Left Join - Hash Cond: (p.k = c.k) - -> Seq Scan on parent p - -> Hash - -> Seq Scan on child c -(5 rows) - --- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling -select p.* from - parent p left join child c on (p.k = c.k) - where p.k = 1 and p.k = 2; - k | pd ----+---- -(0 rows) - -explain (costs off) -select p.* from - parent p left join child c on (p.k = c.k) - where p.k = 1 and p.k = 2; - QUERY PLAN --------------------------- - Result - Replaces: Scan on p - One-Time Filter: false -(3 rows) - -select p.* from - (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k - where p.k = 1 and p.k = 2; - k | pd ----+---- -(0 rows) - -explain (costs off) -select p.* from - (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k - where p.k = 1 and p.k = 2; - QUERY PLAN --------------------------- - Result - Replaces: Join on p, x - One-Time Filter: false -(3 rows) - --- bug 5255: this is not optimizable by join removal -begin; -CREATE TEMP TABLE a (id int PRIMARY KEY); -CREATE TEMP TABLE b (id int PRIMARY KEY, a_id int); -INSERT INTO a VALUES (0), (1); -INSERT INTO b VALUES (0, 0), (1, NULL); -SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); - id | a_id | id -----+------+---- - 1 | | -(1 row) - -SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0); - id | a_id -----+------ - 1 | -(1 row) - -rollback; --- another join removal bug: this is not optimizable, either -begin; -create temp table innertab (id int8 primary key, dat1 int8); -insert into innertab values(123, 42); -SELECT * FROM - (SELECT 1 AS x) ss1 - LEFT JOIN - (SELECT q1, q2, COALESCE(dat1, q1) AS y - FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2 - ON true; - x | q1 | q2 | y ----+------------------+-------------------+------------------ - 1 | 123 | 456 | 123 - 1 | 123 | 4567890123456789 | 123 - 1 | 4567890123456789 | 123 | 42 - 1 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 1 | 4567890123456789 | -4567890123456789 | 4567890123456789 -(5 rows) - --- join removal bug #17769: can't remove if there's a pushed-down reference -EXPLAIN (COSTS OFF) -SELECT q2 FROM - (SELECT * - FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss - WHERE COALESCE(dat1, 0) = q1; - QUERY PLAN ----------------------------------------------------------------- - Nested Loop Left Join - Filter: (COALESCE(innertab.dat1, '0'::bigint) = int8_tbl.q1) - -> Seq Scan on int8_tbl - -> Index Scan using innertab_pkey on innertab - Index Cond: (id = int8_tbl.q2) -(5 rows) - --- join removal bug #17773: otherwise-removable PHV appears in a qual condition -EXPLAIN (VERBOSE, COSTS OFF) -SELECT q2 FROM - (SELECT q2, 'constant'::text AS x - FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss - RIGHT JOIN int4_tbl ON NULL - WHERE x >= x; - QUERY PLAN ------------------------------------------------------- - Nested Loop Left Join - Output: int8_tbl.q2 - Join Filter: NULL::boolean - Filter: (('constant'::text) >= ('constant'::text)) - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 - -> Result - Output: int8_tbl.q2, 'constant'::text - Replaces: Scan on int8_tbl - One-Time Filter: false -(10 rows) - --- join removal bug #17786: check that OR conditions are cleaned up -EXPLAIN (COSTS OFF) -SELECT f1, x -FROM int4_tbl - JOIN ((SELECT 42 AS x FROM int8_tbl LEFT JOIN innertab ON q1 = id) AS ss1 - RIGHT JOIN tenk1 ON NULL) - ON tenk1.unique1 = ss1.x OR tenk1.unique2 = ss1.x; - QUERY PLAN --------------------------------------------------------------------------- - Nested Loop - -> Seq Scan on int4_tbl - -> Materialize - -> Nested Loop Left Join - Join Filter: NULL::boolean - Filter: ((tenk1.unique1 = (42)) OR (tenk1.unique2 = (42))) - -> Seq Scan on tenk1 - -> Result - Replaces: Scan on int8_tbl - One-Time Filter: false -(10 rows) - -rollback; --- another join removal bug: we must clean up correctly when removing a PHV -begin; -create temp table uniquetbl (f1 text unique); -explain (costs off) -select t1.* from - uniquetbl as t1 - left join (select *, '***'::text as d1 from uniquetbl) t2 - on t1.f1 = t2.f1 - left join uniquetbl t3 - on t2.d1 = t3.f1; - QUERY PLAN --------------------------- - Seq Scan on uniquetbl t1 -(1 row) - -explain (costs off) -select t0.* -from - text_tbl t0 - left join - (select case t1.ten when 0 then 'doh!'::text else null::text end as case1, - t1.stringu2 - from tenk1 t1 - join int4_tbl i4 ON i4.f1 = t1.unique2 - left join uniquetbl u1 ON u1.f1 = t1.string4) ss - on t0.f1 = ss.case1 -where ss.stringu2 !~* ss.case1; - QUERY PLAN --------------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (t0.f1 = CASE t1.ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END) - -> Nested Loop - -> Seq Scan on int4_tbl i4 - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: (unique2 = i4.f1) - Filter: (stringu2 !~* CASE ten WHEN 0 THEN 'doh!'::text ELSE NULL::text END) - -> Materialize - -> Seq Scan on text_tbl t0 -(9 rows) - -select t0.* -from - text_tbl t0 - left join - (select case t1.ten when 0 then 'doh!'::text else null::text end as case1, - t1.stringu2 - from tenk1 t1 - join int4_tbl i4 ON i4.f1 = t1.unique2 - left join uniquetbl u1 ON u1.f1 = t1.string4) ss - on t0.f1 = ss.case1 -where ss.stringu2 !~* ss.case1; - f1 ------- - doh! -(1 row) - -rollback; --- another join removal bug: we must clean up EquivalenceClasses too -begin; -create temp table t (a int unique); -insert into t values (1); -explain (costs off) -select 1 -from t t1 - left join (select 2 as c - from t t2 left join t t3 on t2.a = t3.a) s - on true -where t1.a = s.c; - QUERY PLAN ------------------------------- - Nested Loop Left Join - Filter: (t1.a = (2)) - -> Seq Scan on t t1 - -> Materialize - -> Seq Scan on t t2 -(5 rows) - -select 1 -from t t1 - left join (select 2 as c - from t t2 left join t t3 on t2.a = t3.a) s - on true -where t1.a = s.c; - ?column? ----------- -(0 rows) - -rollback; --- check handling of semijoins after join removal: we must suppress --- unique-ification of known-constant values -begin; -create temp table t (a int unique, b int); -insert into t values (1, 2); -explain (verbose, costs off) -select t1.a from t t1 - left join t t2 on t1.a = t2.a - join t t3 on true -where exists (select 1 from t t4 - join t t5 on t4.b = t5.b - join t t6 on t5.b = t6.b - where t1.a = t4.a and t3.a = t5.a and t4.a = 1); - QUERY PLAN ------------------------------------------------------------------------------------- - Nested Loop - Output: t1.a - Inner Unique: true - -> Nested Loop - Output: t1.a, t5.a - -> Index Only Scan using t_a_key on pg_temp.t t1 - Output: t1.a - Index Cond: (t1.a = 1) - -> HashAggregate - Output: t5.a - Group Key: t5.a - -> Hash Join - Output: t5.a - Hash Cond: (t6.b = t4.b) - -> Seq Scan on pg_temp.t t6 - Output: t6.a, t6.b - -> Hash - Output: t4.b, t5.b, t5.a - -> Hash Join - Output: t4.b, t5.b, t5.a - Inner Unique: true - Hash Cond: (t5.b = t4.b) - -> Seq Scan on pg_temp.t t5 - Output: t5.a, t5.b - -> Hash - Output: t4.b, t4.a - -> Index Scan using t_a_key on pg_temp.t t4 - Output: t4.b, t4.a - Index Cond: (t4.a = 1) - -> Index Only Scan using t_a_key on pg_temp.t t3 - Output: t3.a - Index Cond: (t3.a = t5.a) -(32 rows) - -select t1.a from t t1 - left join t t2 on t1.a = t2.a - join t t3 on true -where exists (select 1 from t t4 - join t t5 on t4.b = t5.b - join t t6 on t5.b = t6.b - where t1.a = t4.a and t3.a = t5.a and t4.a = 1); - a ---- - 1 -(1 row) - -rollback; --- check handling of semijoins if all RHS columns are equated to constants: we --- should suppress unique-ification in this case. -begin; -create temp table t (a int, b int); -insert into t values (1, 2); -explain (costs off) -select * from t t1, t t2 where exists - (select 1 from t t3 where t1.a = t3.a and t2.b = t3.b and t3.a = 1 and t3.b = 2); - QUERY PLAN ---------------------------------------------- - Nested Loop Semi Join - -> Nested Loop - -> Seq Scan on t t1 - Filter: (a = 1) - -> Materialize - -> Seq Scan on t t2 - Filter: (b = 2) - -> Materialize - -> Seq Scan on t t3 - Filter: ((a = 1) AND (b = 2)) -(10 rows) - -select * from t t1, t t2 where exists - (select 1 from t t3 where t1.a = t3.a and t2.b = t3.b and t3.a = 1 and t3.b = 2); - a | b | a | b ----+---+---+--- - 1 | 2 | 1 | 2 -(1 row) - -rollback; --- check handling of semijoin unique-ification for child relations if all RHS --- columns are equated to constants. -begin; -create temp table p (a int, b int) partition by range (a); -create temp table p1 partition of p for values from (0) to (10); -create temp table p2 partition of p for values from (10) to (20); -insert into p values (1, 2); -insert into p values (10, 20); -set enable_partitionwise_join to on; -explain (costs off) -select * from p t1 where exists - (select 1 from p t2 where t1.a = t2.a and t1.a = 1); - QUERY PLAN -------------------------------- - Nested Loop Semi Join - -> Seq Scan on p1 t1 - Filter: (a = 1) - -> Materialize - -> Seq Scan on p1 t2 - Filter: (a = 1) -(6 rows) - -select * from p t1 where exists - (select 1 from p t2 where t1.a = t2.a and t1.a = 1); - a | b ----+--- - 1 | 2 -(1 row) - -rollback; --- test cases where we can remove a join, but not a PHV computed at it -begin; -create temp table t (a int unique, b int); -insert into t values (1,1), (2,2); -explain (costs off) -select 1 -from t t1 - left join (select t2.a, 1 as c - from t t2 left join t t3 on t2.a = t3.a) s - on true - left join t t4 on true -where s.a < s.c; - QUERY PLAN -------------------------------------- - Nested Loop Left Join - -> Nested Loop - -> Seq Scan on t t1 - -> Materialize - -> Seq Scan on t t2 - Filter: (a < 1) - -> Materialize - -> Seq Scan on t t4 -(8 rows) - -explain (costs off) -select t1.a, s.* -from t t1 - left join lateral (select t2.a, coalesce(t1.a, 1) as c - from t t2 left join t t3 on t2.a = t3.a) s - on true - left join t t4 on true -where s.a < s.c; - QUERY PLAN ------------------------------------------------ - Nested Loop Left Join - -> Nested Loop - -> Seq Scan on t t1 - -> Seq Scan on t t2 - Filter: (a < COALESCE(t1.a, 1)) - -> Materialize - -> Seq Scan on t t4 -(7 rows) - -select t1.a, s.* -from t t1 - left join lateral (select t2.a, coalesce(t1.a, 1) as c - from t t2 left join t t3 on t2.a = t3.a) s - on true - left join t t4 on true -where s.a < s.c; - a | a | c ----+---+--- - 2 | 1 | 2 - 2 | 1 | 2 -(2 rows) - -rollback; --- test case to expose miscomputation of required relid set for a PHV -explain (verbose, costs off) -select i8.*, ss.v, t.unique2 - from int8_tbl i8 - left join int4_tbl i4 on i4.f1 = 1 - left join lateral (select i4.f1 + 1 as v) as ss on true - left join tenk1 t on t.unique2 = ss.v -where q2 = 456; - QUERY PLAN -------------------------------------------------------------- - Nested Loop Left Join - Output: i8.q1, i8.q2, ((i4.f1 + 1)), t.unique2 - -> Nested Loop Left Join - Output: i8.q1, i8.q2, (i4.f1 + 1) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - Filter: (i8.q2 = 456) - -> Seq Scan on public.int4_tbl i4 - Output: i4.f1 - Filter: (i4.f1 = 1) - -> Index Only Scan using tenk1_unique2 on public.tenk1 t - Output: t.unique2 - Index Cond: (t.unique2 = ((i4.f1 + 1))) -(13 rows) - -select i8.*, ss.v, t.unique2 - from int8_tbl i8 - left join int4_tbl i4 on i4.f1 = 1 - left join lateral (select i4.f1 + 1 as v) as ss on true - left join tenk1 t on t.unique2 = ss.v -where q2 = 456; - q1 | q2 | v | unique2 ------+-----+---+--------- - 123 | 456 | | -(1 row) - --- and check a related issue where we miscompute required relids for --- a PHV that's been translated to a child rel -create temp table parttbl (a integer primary key) partition by range (a); -create temp table parttbl1 partition of parttbl for values from (1) to (100); -insert into parttbl values (11), (12); -explain (costs off) -select * from - (select *, 12 as phv from parttbl) as ss - right join int4_tbl on true -where ss.a = ss.phv and f1 = 0; - QUERY PLAN ------------------------------------- - Nested Loop - -> Seq Scan on parttbl1 parttbl - Filter: (a = 12) - -> Seq Scan on int4_tbl - Filter: (f1 = 0) -(5 rows) - -select * from - (select *, 12 as phv from parttbl) as ss - right join int4_tbl on true -where ss.a = ss.phv and f1 = 0; - a | phv | f1 -----+-----+---- - 12 | 12 | 0 -(1 row) - --- bug #8444: we've historically allowed duplicate aliases within aliased JOINs -select * from - int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error -ERROR: column reference "f1" is ambiguous -LINE 2: ..._tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; - ^ -select * from - int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = y.f1; -- error -ERROR: invalid reference to FROM-clause entry for table "y" -LINE 2: ...bl x join (int4_tbl x cross join int4_tbl y) j on q1 = y.f1; - ^ -DETAIL: There is an entry for table "y", but it cannot be referenced from this part of the query. -select * from - int8_tbl x join (int4_tbl x cross join int4_tbl y(ff)) j on q1 = f1; -- ok - q1 | q2 | f1 | ff -----+----+----+---- -(0 rows) - --- --- test that semi- or inner self-joins on a unique column are removed --- --- enable only nestloop to get more predictable plans -set enable_hashjoin to off; -set enable_mergejoin to off; -create table sj (a int unique, b int, c int unique); -insert into sj values (1, null, 2), (null, 2, null), (2, 1, 1); -analyze sj; --- Trivial self-join case. -explain (costs off) -select p.* from sj p, sj q where q.a = p.a and q.b = q.a - 1; - QUERY PLAN ------------------------------------------------ - Seq Scan on sj q - Filter: ((a IS NOT NULL) AND (b = (a - 1))) -(2 rows) - -select p.* from sj p, sj q where q.a = p.a and q.b = q.a - 1; - a | b | c ----+---+--- - 2 | 1 | 1 -(1 row) - --- Self-join removal performs after a subquery pull-up process and could remove --- such kind of self-join too. Check this option. -explain (costs off) -select * from sj p -where exists (select * from sj q - where q.a = p.a and q.b < 10); - QUERY PLAN ------------------------------------------- - Seq Scan on sj q - Filter: ((a IS NOT NULL) AND (b < 10)) -(2 rows) - -select * from sj p -where exists (select * from sj q - where q.a = p.a and q.b < 10); - a | b | c ----+---+--- - 2 | 1 | 1 -(1 row) - --- Don't remove self-join for the case of equality of two different unique columns. -explain (costs off) -select * from sj t1, sj t2 where t1.a = t2.c and t1.b is not null; - QUERY PLAN ---------------------------------------- - Nested Loop - Join Filter: (t1.a = t2.c) - -> Seq Scan on sj t2 - -> Materialize - -> Seq Scan on sj t1 - Filter: (b IS NOT NULL) -(6 rows) - --- Ensure that relations with TABLESAMPLE clauses are not considered as --- candidates to be removed -explain (costs off) -select * from sj t1 - join lateral - (select * from sj tablesample system(t1.b)) s - on t1.a = s.a; - QUERY PLAN ---------------------------------------- - Nested Loop - -> Seq Scan on sj t1 - -> Memoize - Cache Key: t1.a, t1.b - Cache Mode: binary - -> Sample Scan on sj - Sampling: system (t1.b) - Filter: (t1.a = a) -(8 rows) - --- Ensure that SJE does not form a self-referential lateral dependency -explain (costs off) -select * from sj t1 - left join lateral - (select t1.a as t1a, * from sj t2) s - on true -where t1.a = s.a; - QUERY PLAN ---------------------------- - Seq Scan on sj t2 - Filter: (a IS NOT NULL) -(2 rows) - --- Degenerated case. -explain (costs off) -select * from - (select a as x from sj where false) as q1, - (select a as y from sj where false) as q2 -where q1.x = q2.y; - QUERY PLAN --------------------------- - Result - Replaces: Scan on sj - One-Time Filter: false -(3 rows) - --- We can't use a cross-EC generated self join qual because of current logic of --- the generate_join_implied_equalities routine. -explain (costs off) -select * from sj t1, sj t2 where t1.a = t1.b and t1.b = t2.b and t2.b = t2.a; - QUERY PLAN ------------------------------- - Nested Loop - Join Filter: (t1.a = t2.b) - -> Seq Scan on sj t1 - Filter: (a = b) - -> Seq Scan on sj t2 - Filter: (b = a) -(6 rows) - -explain (costs off) -select * from sj t1, sj t2, sj t3 -where t1.a = t1.b and t1.b = t2.b and t2.b = t2.a and - t1.b = t3.b and t3.b = t3.a; - QUERY PLAN ------------------------------------- - Nested Loop - Join Filter: (t1.a = t3.b) - -> Nested Loop - Join Filter: (t1.a = t2.b) - -> Seq Scan on sj t1 - Filter: (a = b) - -> Seq Scan on sj t2 - Filter: (b = a) - -> Seq Scan on sj t3 - Filter: (b = a) -(10 rows) - --- Double self-join removal. --- Use a condition on "b + 1", not on "b", for the second join, so that --- the equivalence class is different from the first one, and we can --- test the non-ec code path. -explain (costs off) -select * -from sj t1 - join sj t2 on t1.a = t2.a and t1.b = t2.b - join sj t3 on t2.a = t3.a and t2.b + 1 = t3.b + 1; - QUERY PLAN ---------------------------------------------------------------------------- - Seq Scan on sj t3 - Filter: ((a IS NOT NULL) AND (b IS NOT NULL) AND ((b + 1) IS NOT NULL)) -(2 rows) - --- subselect that references the removed relation -explain (costs off) -select t1.a, (select a from sj where a = t2.a and a = t1.a) -from sj t1, sj t2 -where t1.a = t2.a; - QUERY PLAN ------------------------------------------- - Seq Scan on sj t2 - Filter: (a IS NOT NULL) - SubPlan expr_1 - -> Result - One-Time Filter: (t2.a = t2.a) - -> Seq Scan on sj - Filter: (a = t2.a) -(7 rows) - --- self-join under outer join -explain (costs off) -select * from sj x join sj y on x.a = y.a -left join int8_tbl z on x.a = z.q1; - QUERY PLAN ------------------------------------- - Nested Loop Left Join - Join Filter: (y.a = z.q1) - -> Seq Scan on sj y - Filter: (a IS NOT NULL) - -> Materialize - -> Seq Scan on int8_tbl z -(6 rows) - -explain (costs off) -select * from sj x join sj y on x.a = y.a -left join int8_tbl z on y.a = z.q1; - QUERY PLAN ------------------------------------- - Nested Loop Left Join - Join Filter: (y.a = z.q1) - -> Seq Scan on sj y - Filter: (a IS NOT NULL) - -> Materialize - -> Seq Scan on int8_tbl z -(6 rows) - -explain (costs off) -select * from ( - select t1.*, t2.a as ax from sj t1 join sj t2 - on (t1.a = t2.a and t1.c * t1.c = t2.c + 2 and t2.b is null) -) as q1 -left join - (select t3.* from sj t3, sj t4 where t3.c = t4.c) as q2 -on q1.ax = q2.a; - QUERY PLAN ---------------------------------------------------------------------------- - Nested Loop Left Join - Join Filter: (t2.a = t4.a) - -> Seq Scan on sj t2 - Filter: ((b IS NULL) AND (a IS NOT NULL) AND ((c * c) = (c + 2))) - -> Seq Scan on sj t4 - Filter: (c IS NOT NULL) -(6 rows) - --- Test that placeholders are updated correctly after join removal -explain (costs off) -select * from (values (1)) x -left join (select coalesce(y.q1, 1) from int8_tbl y - right join sj j1 inner join sj j2 on j1.a = j2.a - on true) z -on true; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - -> Result - -> Nested Loop Left Join - -> Seq Scan on sj j2 - Filter: (a IS NOT NULL) - -> Materialize - -> Seq Scan on int8_tbl y -(7 rows) - --- Test that references to the removed rel in lateral subqueries are replaced --- correctly after join removal -explain (verbose, costs off) -select t3.a from sj t1 - join sj t2 on t1.a = t2.a - join lateral (select t1.a offset 0) t3 on true; - QUERY PLAN ------------------------------------- - Nested Loop - Output: (t2.a) - -> Seq Scan on public.sj t2 - Output: t2.a, t2.b, t2.c - Filter: (t2.a IS NOT NULL) - -> Result - Output: t2.a -(7 rows) - -explain (verbose, costs off) -select t3.a from sj t1 - join sj t2 on t1.a = t2.a - join lateral (select * from (select t1.a offset 0) offset 0) t3 on true; - QUERY PLAN ------------------------------------- - Nested Loop - Output: (t2.a) - -> Seq Scan on public.sj t2 - Output: t2.a, t2.b, t2.c - Filter: (t2.a IS NOT NULL) - -> Result - Output: t2.a -(7 rows) - -explain (verbose, costs off) -select t4.a from sj t1 - join sj t2 on t1.a = t2.a - join lateral (select t3.a from sj t3, (select t1.a) offset 0) t4 on true; - QUERY PLAN ------------------------------------- - Nested Loop - Output: t3.a - -> Seq Scan on public.sj t2 - Output: t2.a, t2.b, t2.c - Filter: (t2.a IS NOT NULL) - -> Seq Scan on public.sj t3 - Output: t3.a -(7 rows) - --- Check updating of semi_rhs_exprs links from upper-level semi join to --- the removing relation -explain (verbose, costs off) -select t1.a from sj t1 where t1.b in ( - select t2.b from sj t2 join sj t3 on t2.c=t3.c); - QUERY PLAN ------------------------------------------- - Nested Loop Semi Join - Output: t1.a - Join Filter: (t1.b = t3.b) - -> Seq Scan on public.sj t1 - Output: t1.a, t1.b, t1.c - -> Materialize - Output: t3.c, t3.b - -> Seq Scan on public.sj t3 - Output: t3.c, t3.b - Filter: (t3.c IS NOT NULL) -(10 rows) - --- --- SJE corner case: uniqueness of an inner is [partially] derived from --- baserestrictinfo clauses. --- XXX: We really should allow SJE for these corner cases? --- -INSERT INTO sj VALUES (3, 1, 3); --- Don't remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 2 AND j2.a = 3; - QUERY PLAN ------------------------------- - Nested Loop - Join Filter: (j1.b = j2.b) - -> Seq Scan on sj j1 - Filter: (a = 2) - -> Seq Scan on sj j2 - Filter: (a = 3) -(6 rows) - --- Return one row -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 2 AND j2.a = 3; - a | b | c | a | b | c ----+---+---+---+---+--- - 2 | 1 | 1 | 3 | 1 | 3 -(1 row) - --- Remove SJ, define uniqueness by a constant -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 2 AND j2.a = 2; - QUERY PLAN ------------------------------------------ - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND (a = 2)) -(2 rows) - --- Return one row -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 2 AND j2.a = 2; - a | b | c | a | b | c ----+---+---+---+---+--- - 2 | 1 | 1 | 2 | 1 | 1 -(1 row) - --- Remove SJ, define uniqueness by a constant expression -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 -WHERE j1.b = j2.b - AND j1.a = (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int - AND (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int = j2.a; - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------- - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND (a = (((EXTRACT(dow FROM CURRENT_TIMESTAMP(0)) / '15'::numeric) + '3'::numeric))::integer)) -(2 rows) - --- Return one row -SELECT * FROM sj j1, sj j2 -WHERE j1.b = j2.b - AND j1.a = (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int - AND (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int = j2.a; - a | b | c | a | b | c ----+---+---+---+---+--- - 3 | 1 | 3 | 3 | 1 | 3 -(1 row) - --- Remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 1 AND j2.a = 1; - QUERY PLAN ------------------------------------------ - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND (a = 1)) -(2 rows) - --- Return no rows -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 1 AND j2.a = 1; - a | b | c | a | b | c ----+---+---+---+---+--- -(0 rows) - --- Shuffle a clause. Remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND 1 = j1.a AND j2.a = 1; - QUERY PLAN ------------------------------------------ - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND (a = 1)) -(2 rows) - --- Return no rows -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND 1 = j1.a AND j2.a = 1; - a | b | c | a | b | c ----+---+---+---+---+--- -(0 rows) - --- SJE Corner case: a 'a.x=a.x' clause, have replaced with 'a.x IS NOT NULL' --- after SJ elimination it shouldn't be a mergejoinable clause. -EXPLAIN (COSTS OFF) -SELECT t4.* -FROM (SELECT t1.*, t2.a AS a1 FROM sj t1, sj t2 WHERE t1.b = t2.b) AS t3 -JOIN sj t4 ON (t4.a = t3.a) WHERE t3.a1 = 42; - QUERY PLAN ---------------------------------- - Nested Loop - Join Filter: (t1.b = t2.b) - -> Seq Scan on sj t2 - Filter: (a = 42) - -> Seq Scan on sj t1 - Filter: (a IS NOT NULL) -(6 rows) - -SELECT t4.* -FROM (SELECT t1.*, t2.a AS a1 FROM sj t1, sj t2 WHERE t1.b = t2.b) AS t3 -JOIN sj t4 ON (t4.a = t3.a) WHERE t3.a1 = 42; - a | b | c ----+---+--- -(0 rows) - --- Functional index -CREATE UNIQUE INDEX sj_fn_idx ON sj((a * a)); --- Remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 - WHERE j1.b = j2.b AND j1.a*j1.a = 1 AND j2.a*j2.a = 1; - QUERY PLAN ------------------------------------------------ - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND ((a * a) = 1)) -(2 rows) - --- Don't remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 - WHERE j1.b = j2.b AND j1.a*j1.a = 1 AND j2.a*j2.a = 2; - QUERY PLAN -------------------------------- - Nested Loop - Join Filter: (j1.b = j2.b) - -> Seq Scan on sj j1 - Filter: ((a * a) = 1) - -> Seq Scan on sj j2 - Filter: ((a * a) = 2) -(6 rows) - --- Restriction contains expressions in both sides, Remove SJ. -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 -WHERE j1.b = j2.b - AND (j1.a*j1.a) = (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int - AND (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int = (j2.a*j2.a); - QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------- - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND ((a * a) = (((EXTRACT(dow FROM CURRENT_TIMESTAMP(0)) / '15'::numeric) + '3'::numeric))::integer)) -(2 rows) - --- Empty set of rows should be returned -SELECT * FROM sj j1, sj j2 -WHERE j1.b = j2.b - AND (j1.a*j1.a) = (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int - AND (EXTRACT(DOW FROM current_timestamp(0))/15 + 3)::int = (j2.a*j2.a); - a | b | c | a | b | c ----+---+---+---+---+--- -(0 rows) - --- Restriction contains volatile function - disable SJE feature. -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 -WHERE j1.b = j2.b - AND (j1.a*j1.c/3) = (random()/3 + 3)::int - AND (random()/3 + 3)::int = (j2.a*j2.c/3); - QUERY PLAN ------------------------------------------------------------------------------------------------------------ - Nested Loop - Join Filter: (j1.b = j2.b) - -> Seq Scan on sj j1 - Filter: (((a * c) / 3) = (((random() / '3'::double precision) + '3'::double precision))::integer) - -> Seq Scan on sj j2 - Filter: ((((random() / '3'::double precision) + '3'::double precision))::integer = ((a * c) / 3)) -(6 rows) - --- Return one row -SELECT * FROM sj j1, sj j2 -WHERE j1.b = j2.b - AND (j1.a*j1.c/3) = (random()/3 + 3)::int - AND (random()/3 + 3)::int = (j2.a*j2.c/3); - a | b | c | a | b | c ----+---+---+---+---+--- - 3 | 1 | 3 | 3 | 1 | 3 -(1 row) - --- Multiple filters -CREATE UNIQUE INDEX sj_temp_idx1 ON sj(a,b,c); --- Remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 - WHERE j1.b = j2.b AND j1.a = 2 AND j1.c = 3 AND j2.a = 2 AND 3 = j2.c; - QUERY PLAN ------------------------------------------------------ - Seq Scan on sj j2 - Filter: ((b IS NOT NULL) AND (a = 2) AND (c = 3)) -(2 rows) - --- Don't remove SJ -EXPLAIN (COSTS OFF) - SELECT * FROM sj j1, sj j2 - WHERE j1.b = j2.b AND 2 = j1.a AND j1.c = 3 AND j2.a = 1 AND 3 = j2.c; - QUERY PLAN ---------------------------------------- - Nested Loop - Join Filter: (j1.b = j2.b) - -> Seq Scan on sj j1 - Filter: ((2 = a) AND (c = 3)) - -> Seq Scan on sj j2 - Filter: ((c = 3) AND (a = 1)) -(6 rows) - -CREATE UNIQUE INDEX sj_temp_idx ON sj(a,b); --- Don't remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND j1.a = 2; - QUERY PLAN ------------------------------- - Nested Loop - Join Filter: (j1.b = j2.b) - -> Seq Scan on sj j1 - Filter: (a = 2) - -> Seq Scan on sj j2 -(5 rows) - --- Don't remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND 2 = j2.a; - QUERY PLAN ------------------------------- - Nested Loop - Join Filter: (j1.b = j2.b) - -> Seq Scan on sj j2 - Filter: (2 = a) - -> Seq Scan on sj j1 -(5 rows) - --- Don't remove SJ -EXPLAIN (COSTS OFF) -SELECT * FROM sj j1, sj j2 WHERE j1.b = j2.b AND (j1.a = 1 OR j2.a = 1); - QUERY PLAN ---------------------------------------------------------------- - Nested Loop - Join Filter: ((j1.b = j2.b) AND ((j1.a = 1) OR (j2.a = 1))) - -> Seq Scan on sj j1 - -> Materialize - -> Seq Scan on sj j2 -(5 rows) - -DROP INDEX sj_fn_idx, sj_temp_idx1, sj_temp_idx; --- Test that OR predicated are updated correctly after join removal -CREATE TABLE tab_with_flag ( id INT PRIMARY KEY, is_flag SMALLINT); -CREATE INDEX idx_test_is_flag ON tab_with_flag (is_flag); -EXPLAIN (COSTS OFF) -SELECT COUNT(*) FROM tab_with_flag -WHERE - (is_flag IS NULL OR is_flag = 0) - AND id IN (SELECT id FROM tab_with_flag WHERE id IN (2, 3)); - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Bitmap Heap Scan on tab_with_flag - Recheck Cond: (id = ANY ('{2,3}'::integer[])) - Filter: ((is_flag IS NULL) OR (is_flag = 0)) - -> Bitmap Index Scan on tab_with_flag_pkey - Index Cond: (id = ANY ('{2,3}'::integer[])) -(6 rows) - -DROP TABLE tab_with_flag; --- HAVING clause -explain (costs off) -select p.b from sj p join sj q on p.a = q.a group by p.b having sum(p.a) = 1; - QUERY PLAN ---------------------------------- - HashAggregate - Group Key: q.b - Filter: (sum(q.a) = 1) - -> Seq Scan on sj q - Filter: (a IS NOT NULL) -(5 rows) - --- update lateral references and range table entry reference -explain (verbose, costs off) -select 1 from (select x.* from sj x, sj y where x.a = y.a) q, - lateral generate_series(1, q.a) gs(i); - QUERY PLAN ------------------------------------------------------- - Nested Loop - Output: 1 - -> Seq Scan on public.sj y - Output: y.a, y.b, y.c - Filter: (y.a IS NOT NULL) - -> Function Scan on pg_catalog.generate_series gs - Output: gs.i - Function Call: generate_series(1, y.a) -(8 rows) - -explain (verbose, costs off) -select 1 from (select y.* from sj x, sj y where x.a = y.a) q, - lateral generate_series(1, q.a) gs(i); - QUERY PLAN ------------------------------------------------------- - Nested Loop - Output: 1 - -> Seq Scan on public.sj y - Output: y.a, y.b, y.c - Filter: (y.a IS NOT NULL) - -> Function Scan on pg_catalog.generate_series gs - Output: gs.i - Function Call: generate_series(1, y.a) -(8 rows) - --- Test that a non-EC-derived join clause is processed correctly. Use an --- outer join so that we can't form an EC. -explain (costs off) select * from sj p join sj q on p.a = q.a - left join sj r on p.a + q.a = r.a; - QUERY PLAN ------------------------------------- - Nested Loop Left Join - Join Filter: ((q.a + q.a) = r.a) - -> Seq Scan on sj q - Filter: (a IS NOT NULL) - -> Materialize - -> Seq Scan on sj r -(6 rows) - --- FIXME this constant false filter doesn't look good. Should we merge --- equivalence classes? -explain (costs off) -select * from sj p, sj q where p.a = q.a and p.b = 1 and q.b = 2; - QUERY PLAN ------------------------------------------------------ - Seq Scan on sj q - Filter: ((a IS NOT NULL) AND (b = 2) AND (b = 1)) -(2 rows) - --- Check that attr_needed is updated correctly after self-join removal. In this --- test, the join of j1 with j2 is removed. k1.b is required at either j1 or j2. --- If this info is lost, join targetlist for (k1, k2) will not contain k1.b. --- Use index scan for k1 so that we don't get 'b' from physical tlist used for --- seqscan. Also disable reordering of joins because this test depends on a --- particular join tree. -create table sk (a int, b int); -create index on sk(a); -set join_collapse_limit to 1; -set enable_seqscan to off; -explain (costs off) select 1 from - (sk k1 join sk k2 on k1.a = k2.a) - join (sj j1 join sj j2 on j1.a = j2.a) on j1.b = k1.b; - QUERY PLAN ------------------------------------------------------ - Nested Loop - Join Filter: (k1.b = j2.b) - -> Nested Loop - -> Index Scan using sk_a_idx on sk k1 - -> Index Only Scan using sk_a_idx on sk k2 - Index Cond: (a = k1.a) - -> Materialize - -> Index Scan using sj_a_key on sj j2 - Index Cond: (a IS NOT NULL) -(9 rows) - -explain (costs off) select 1 from - (sk k1 join sk k2 on k1.a = k2.a) - join (sj j1 join sj j2 on j1.a = j2.a) on j2.b = k1.b; - QUERY PLAN ------------------------------------------------------ - Nested Loop - Join Filter: (k1.b = j2.b) - -> Nested Loop - -> Index Scan using sk_a_idx on sk k1 - -> Index Only Scan using sk_a_idx on sk k2 - Index Cond: (a = k1.a) - -> Materialize - -> Index Scan using sj_a_key on sj j2 - Index Cond: (a IS NOT NULL) -(9 rows) - -reset join_collapse_limit; -reset enable_seqscan; --- Check that clauses from the join filter list is not lost on the self-join removal -CREATE TABLE emp1 (id SERIAL PRIMARY KEY NOT NULL, code int); -EXPLAIN (VERBOSE, COSTS OFF) -SELECT * FROM emp1 e1, emp1 e2 WHERE e1.id = e2.id AND e2.code <> e1.code; - QUERY PLAN ------------------------------------------- - Seq Scan on public.emp1 e2 - Output: e2.id, e2.code, e2.id, e2.code - Filter: (e2.code <> e2.code) -(3 rows) - --- Shuffle self-joined relations. Only in the case of iterative deletion --- attempts explains of these queries will be identical. -CREATE UNIQUE INDEX ON emp1((id*id)); -EXPLAIN (COSTS OFF) -SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3 -WHERE c1.id=c2.id AND c1.id*c2.id=c3.id*c3.id; - QUERY PLAN ------------------------------------------ - Aggregate - -> Seq Scan on emp1 c3 - Filter: ((id * id) IS NOT NULL) -(3 rows) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3 -WHERE c1.id=c3.id AND c1.id*c3.id=c2.id*c2.id; - QUERY PLAN ------------------------------------------ - Aggregate - -> Seq Scan on emp1 c3 - Filter: ((id * id) IS NOT NULL) -(3 rows) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM emp1 c1, emp1 c2, emp1 c3 -WHERE c3.id=c2.id AND c3.id*c2.id=c1.id*c1.id; - QUERY PLAN ------------------------------------------ - Aggregate - -> Seq Scan on emp1 c3 - Filter: ((id * id) IS NOT NULL) -(3 rows) - --- Check the usage of a parse tree by the set operations (bug #18170) -EXPLAIN (COSTS OFF) -SELECT c1.code FROM emp1 c1 LEFT JOIN emp1 c2 ON c1.id = c2.id -WHERE c2.id IS NOT NULL -EXCEPT ALL -SELECT c3.code FROM emp1 c3; - QUERY PLAN ---------------------------- - HashSetOp Except All - -> Seq Scan on emp1 c2 - -> Seq Scan on emp1 c3 -(3 rows) - --- Check that SJE removes references from PHVs correctly -explain (costs off) -select * from emp1 t1 left join - (select coalesce(t3.code, 1) from emp1 t2 - left join (emp1 t3 join emp1 t4 on t3.id = t4.id) - on true) -on true; - QUERY PLAN ---------------------------------------------- - Nested Loop Left Join - -> Seq Scan on emp1 t1 - -> Materialize - -> Nested Loop Left Join - -> Seq Scan on emp1 t2 - -> Materialize - -> Seq Scan on emp1 t4 -(7 rows) - --- Try PHV, which could potentially be removed completely by SJE, but that's --- not implemented yet. -explain (verbose, costs off) -select 1 from emp1 t1 left join - ((select 1 as x, * from emp1 t2) s1 inner join - (select * from emp1 t3) s2 on s1.id = s2.id) - on true -where s1.x = 1; - QUERY PLAN ----------------------------------------- - Nested Loop - Output: 1 - -> Seq Scan on public.emp1 t1 - Output: t1.id, t1.code - -> Materialize - Output: t3.id - -> Seq Scan on public.emp1 t3 - Output: t3.id - Filter: (1 = 1) -(9 rows) - --- Check that PHVs do not impose any constraints on removing self joins -explain (verbose, costs off) -select * from emp1 t1 join emp1 t2 on t1.id = t2.id left join - lateral (select t1.id as t1id, * from generate_series(1,1) t3) s on true; - QUERY PLAN ----------------------------------------------------------- - Nested Loop Left Join - Output: t2.id, t2.code, t2.id, t2.code, (t2.id), t3.t3 - -> Seq Scan on public.emp1 t2 - Output: t2.id, t2.code - -> Function Scan on pg_catalog.generate_series t3 - Output: t3.t3, t2.id - Function Call: generate_series(1, 1) -(7 rows) - -explain (verbose, costs off) -select * from generate_series(1,10) t1(id) left join - lateral (select t1.id as t1id, t2.id from emp1 t2 join emp1 t3 on t2.id = t3.id) -on true; - QUERY PLAN ------------------------------------------------------- - Nested Loop Left Join - Output: t1.id, (t1.id), t3.id - -> Function Scan on pg_catalog.generate_series t1 - Output: t1.id - Function Call: generate_series(1, 10) - -> Seq Scan on public.emp1 t3 - Output: t3.id, t1.id -(7 rows) - --- This is a degenerate case of PHV usage: it is evaluated and needed inside --- a baserel scan operation that the SJE removes. The PHV in this test should --- be in the filter of parameterized Index Scan: the replace_nestloop_params() --- code will detect if the placeholder list doesn't have a reference to this --- parameter. --- --- NOTE: enable_hashjoin and enable_mergejoin must be disabled. -CREATE TABLE tbl_phv(x int, y int PRIMARY KEY); -CREATE INDEX tbl_phv_idx ON tbl_phv(x); -INSERT INTO tbl_phv (x, y) - SELECT gs, gs FROM generate_series(1,100) AS gs; -VACUUM ANALYZE tbl_phv; -EXPLAIN (COSTS OFF, VERBOSE) -SELECT 1 FROM tbl_phv t1 LEFT JOIN - (SELECT 1 extra, x, y FROM tbl_phv tl) t3 JOIN - (SELECT y FROM tbl_phv tr) t4 - ON t4.y = t3.y -ON true WHERE t3.extra IS NOT NULL AND t3.x = t1.x % 2; - QUERY PLAN ---------------------------------------------------------- - Nested Loop - Output: 1 - -> Seq Scan on public.tbl_phv t1 - Output: t1.x, t1.y - -> Index Scan using tbl_phv_idx on public.tbl_phv tr - Output: tr.x, tr.y - Index Cond: (tr.x = (t1.x % 2)) - Filter: (1 IS NOT NULL) -(8 rows) - -DROP TABLE IF EXISTS tbl_phv; --- Check that SJE replaces join clauses involving the removed rel correctly -explain (costs off) -select * from emp1 t1 - inner join emp1 t2 on t1.id = t2.id - left join emp1 t3 on t1.id > 1 and t1.id < 2; - QUERY PLAN ----------------------------------------------- - Nested Loop Left Join - Join Filter: ((t2.id > 1) AND (t2.id < 2)) - -> Seq Scan on emp1 t2 - -> Materialize - -> Seq Scan on emp1 t3 -(5 rows) - --- Check that SJE doesn't replace the target relation -EXPLAIN (COSTS OFF) -WITH t1 AS (SELECT * FROM emp1) -UPDATE emp1 SET code = t1.code + 1 FROM t1 -WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code; - QUERY PLAN -------------------------------------------------------- - Update on emp1 - -> Nested Loop - -> Seq Scan on emp1 - -> Index Scan using emp1_pkey on emp1 emp1_1 - Index Cond: (id = emp1.id) -(5 rows) - --- Check that SJE correctly replaces relations in OR-clauses -EXPLAIN (COSTS OFF) -SELECT * FROM emp1 t1 - INNER JOIN emp1 t2 ON t1.id = t2.id - LEFT JOIN emp1 t3 ON t1.code = 1 AND (t2.code = t3.code OR t2.code = 1); - QUERY PLAN ---------------------------------------------------------------------------- - Nested Loop Left Join - Join Filter: ((t2.code = 1) AND ((t2.code = t3.code) OR (t2.code = 1))) - -> Seq Scan on emp1 t2 - -> Materialize - -> Seq Scan on emp1 t3 -(5 rows) - - INSERT INTO emp1 VALUES (1, 1), (2, 1); -WITH t1 AS (SELECT * FROM emp1) -UPDATE emp1 SET code = t1.code + 1 FROM t1 -WHERE t1.id = emp1.id RETURNING emp1.id, emp1.code, t1.code; - id | code | code -----+------+------ - 1 | 2 | 1 - 2 | 2 | 1 -(2 rows) - -TRUNCATE emp1; -EXPLAIN (COSTS OFF) -UPDATE sj sq SET b = 1 FROM sj as sz WHERE sq.a = sz.a; - QUERY PLAN -------------------------------------- - Update on sj sq - -> Nested Loop - Join Filter: (sq.a = sz.a) - -> Seq Scan on sj sq - -> Materialize - -> Seq Scan on sj sz -(6 rows) - -CREATE RULE sj_del_rule AS ON DELETE TO sj - DO INSTEAD - UPDATE sj SET a = 1 WHERE a = old.a; -EXPLAIN (COSTS OFF) DELETE FROM sj; - QUERY PLAN --------------------------------------- - Update on sj sj_1 - -> Nested Loop - Join Filter: (sj.a = sj_1.a) - -> Seq Scan on sj sj_1 - -> Materialize - -> Seq Scan on sj -(6 rows) - -DROP RULE sj_del_rule ON sj CASCADE; --- Check that SJE does not mistakenly omit qual clauses (bug #18187) -insert into emp1 values (1, 1); -explain (costs off) -select 1 from emp1 full join - (select * from emp1 t1 join - emp1 t2 join emp1 t3 on t2.id = t3.id - on true - where false) s on true -where false; - QUERY PLAN ----------------------------------- - Result - Replaces: Join on emp1, t1, t3 - One-Time Filter: false -(3 rows) - -select 1 from emp1 full join - (select * from emp1 t1 join - emp1 t2 join emp1 t3 on t2.id = t3.id - on true - where false) s on true -where false; - ?column? ----------- -(0 rows) - --- Check that SJE does not mistakenly re-use knowledge of relation uniqueness --- made with different set of quals -insert into emp1 values (2, 1); -explain (costs off) -select * from emp1 t1 where exists (select * from emp1 t2 - where t2.id = t1.code and t2.code > 0); - QUERY PLAN ---------------------------------------------- - Nested Loop - -> Seq Scan on emp1 t1 - -> Index Scan using emp1_pkey on emp1 t2 - Index Cond: (id = t1.code) - Filter: (code > 0) -(5 rows) - -select * from emp1 t1 where exists (select * from emp1 t2 - where t2.id = t1.code and t2.code > 0); - id | code -----+------ - 1 | 1 - 2 | 1 -(2 rows) - --- We can remove the join even if we find the join can't duplicate rows and --- the base quals of each side are different. In the following case we end up --- moving quals over to s1 to make it so it can't match any rows. -create table sl(a int, b int, c int); -create unique index on sl(a, b); -vacuum analyze sl; --- Both sides are unique, but base quals are different -explain (costs off) -select * from sl t1, sl t2 where t1.a = t2.a and t1.b = 1 and t2.b = 2; - QUERY PLAN ------------------------------- - Nested Loop - Join Filter: (t1.a = t2.a) - -> Seq Scan on sl t1 - Filter: (b = 1) - -> Seq Scan on sl t2 - Filter: (b = 2) -(6 rows) - --- Check NullTest in baserestrictinfo list -explain (costs off) -select * from sl t1, sl t2 -where t1.a = t2.a and t1.b = 1 and t2.b = 2 - and t1.c IS NOT NULL and t2.c IS NOT NULL - and t2.b IS NOT NULL and t1.b IS NOT NULL - and t1.a IS NOT NULL and t2.a IS NOT NULL; - QUERY PLAN ---------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (t1.a = t2.a) - -> Seq Scan on sl t1 - Filter: ((c IS NOT NULL) AND (b IS NOT NULL) AND (a IS NOT NULL) AND (b = 1)) - -> Seq Scan on sl t2 - Filter: ((c IS NOT NULL) AND (b IS NOT NULL) AND (a IS NOT NULL) AND (b = 2)) -(6 rows) - -explain (verbose, costs off) -select * from sl t1, sl t2 -where t1.b = t2.b and t2.a = 3 and t1.a = 3 - and t1.c IS NOT NULL and t2.c IS NOT NULL - and t2.b IS NOT NULL and t1.b IS NOT NULL - and t1.a IS NOT NULL and t2.a IS NOT NULL; - QUERY PLAN ---------------------------------------------------------------------------------------------- - Seq Scan on public.sl t2 - Output: t2.a, t2.b, t2.c, t2.a, t2.b, t2.c - Filter: ((t2.c IS NOT NULL) AND (t2.b IS NOT NULL) AND (t2.a IS NOT NULL) AND (t2.a = 3)) -(3 rows) - --- Join qual isn't mergejoinable, but inner is unique. -EXPLAIN (COSTS OFF) -SELECT n2.a FROM sj n1, sj n2 WHERE n1.a <> n2.a AND n2.a = 1; - QUERY PLAN -------------------------------- - Nested Loop - Join Filter: (n1.a <> n2.a) - -> Seq Scan on sj n2 - Filter: (a = 1) - -> Seq Scan on sj n1 -(5 rows) - -EXPLAIN (COSTS OFF) -SELECT * FROM -(SELECT n2.a FROM sj n1, sj n2 WHERE n1.a <> n2.a) q0, sl -WHERE q0.a = 1; - QUERY PLAN -------------------------------- - Nested Loop - Join Filter: (n1.a <> n2.a) - -> Nested Loop - -> Seq Scan on sl - -> Seq Scan on sj n2 - Filter: (a = 1) - -> Seq Scan on sj n1 -(7 rows) - --- Check optimization disabling if it will violate special join conditions. --- Two identical joined relations satisfies self join removal conditions but --- stay in different special join infos. -CREATE TABLE sj_t1 (id serial, a int); -CREATE TABLE sj_t2 (id serial, a int); -CREATE TABLE sj_t3 (id serial, a int); -CREATE TABLE sj_t4 (id serial, a int); -CREATE UNIQUE INDEX ON sj_t3 USING btree (a,id); -CREATE UNIQUE INDEX ON sj_t2 USING btree (id); -EXPLAIN (COSTS OFF) -SELECT * FROM sj_t1 -JOIN ( - SELECT sj_t2.id AS id FROM sj_t2 - WHERE EXISTS - ( - SELECT TRUE FROM sj_t3,sj_t4 WHERE sj_t3.a = 1 AND sj_t3.id = sj_t2.id - ) - ) t2t3t4 -ON sj_t1.id = t2t3t4.id -JOIN ( - SELECT sj_t2.id AS id FROM sj_t2 - WHERE EXISTS - ( - SELECT TRUE FROM sj_t3,sj_t4 WHERE sj_t3.a = 1 AND sj_t3.id = sj_t2.id - ) - ) _t2t3t4 -ON sj_t1.id = _t2t3t4.id; - QUERY PLAN -------------------------------------------------------------------------------------- - Nested Loop - Join Filter: (sj_t3.id = sj_t1.id) - -> Nested Loop - Join Filter: (sj_t2.id = sj_t3.id) - -> Nested Loop Semi Join - -> Nested Loop - -> HashAggregate - Group Key: sj_t3.id - -> Nested Loop - -> Seq Scan on sj_t4 - -> Materialize - -> Bitmap Heap Scan on sj_t3 - Recheck Cond: (a = 1) - -> Bitmap Index Scan on sj_t3_a_id_idx - Index Cond: (a = 1) - -> Index Only Scan using sj_t2_id_idx on sj_t2 sj_t2_1 - Index Cond: (id = sj_t3.id) - -> Nested Loop - -> Index Only Scan using sj_t3_a_id_idx on sj_t3 sj_t3_1 - Index Cond: ((a = 1) AND (id = sj_t3.id)) - -> Seq Scan on sj_t4 sj_t4_1 - -> Index Only Scan using sj_t2_id_idx on sj_t2 - Index Cond: (id = sj_t2_1.id) - -> Seq Scan on sj_t1 -(24 rows) - --- --- Test RowMarks-related code --- --- Both sides have explicit LockRows marks -EXPLAIN (COSTS OFF) -SELECT a1.a FROM sj a1,sj a2 WHERE (a1.a=a2.a) FOR UPDATE; - QUERY PLAN ---------------------------------- - LockRows - -> Seq Scan on sj a2 - Filter: (a IS NOT NULL) -(3 rows) - -reset enable_hashjoin; -reset enable_mergejoin; --- --- Test hints given on incorrect column references are useful --- -select t1.uunique1 from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t1" suggestion -ERROR: column t1.uunique1 does not exist -LINE 1: select t1.uunique1 from - ^ -HINT: Perhaps you meant to reference the column "t1.unique1". -select t2.uunique1 from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t2" suggestion -ERROR: column t2.uunique1 does not exist -LINE 1: select t2.uunique1 from - ^ -HINT: Perhaps you meant to reference the column "t2.unique1". -select uunique1 from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, suggest both at once -ERROR: column "uunique1" does not exist -LINE 1: select uunique1 from - ^ -HINT: Perhaps you meant to reference the column "t1.unique1" or the column "t2.unique1". -select ctid from - tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, need qualification -ERROR: column "ctid" does not exist -LINE 1: select ctid from - ^ -DETAIL: There are columns named "ctid", but they are in tables that cannot be referenced from this part of the query. -HINT: Try using a table-qualified name. --- --- Take care to reference the correct RTE --- -select atts.relid::regclass, s.* from pg_stats s join - pg_attribute a on s.attname = a.attname and s.tablename = - a.attrelid::regclass::text join (select unnest(indkey) attnum, - indexrelid from pg_index i) atts on atts.attnum = a.attnum where - schemaname != 'pg_catalog'; -ERROR: column atts.relid does not exist -LINE 1: select atts.relid::regclass, s.* from pg_stats s join - ^ --- Test bug in rangetable flattening -explain (verbose, costs off) -select 1 from - (select * from int8_tbl where q1 <> (select 42) offset 0) ss -where false; - QUERY PLAN --------------------------- - Result - Output: 1 - Replaces: Scan on ss - One-Time Filter: false -(4 rows) - --- --- Test LATERAL --- -select unique2, x.* -from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; - unique2 | f1 ----------+---- - 9998 | 0 -(1 row) - -explain (costs off) - select unique2, x.* - from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x; - QUERY PLAN -------------------------------------------------- - Nested Loop - -> Seq Scan on int4_tbl b - -> Index Scan using tenk1_unique1 on tenk1 a - Index Cond: (unique1 = b.f1) -(4 rows) - -select unique2, x.* -from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; - unique2 | f1 ----------+---- - 9998 | 0 -(1 row) - -explain (costs off) - select unique2, x.* - from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss; - QUERY PLAN ------------------------------------------------ - Nested Loop - -> Seq Scan on int4_tbl x - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = x.f1) -(4 rows) - -explain (costs off) - select unique2, x.* - from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss; - QUERY PLAN ------------------------------------------------ - Nested Loop - -> Seq Scan on int4_tbl x - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = x.f1) -(4 rows) - -select unique2, x.* -from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; - unique2 | f1 ----------+------------- - 9998 | 0 - | 123456 - | -123456 - | 2147483647 - | -2147483647 -(5 rows) - -explain (costs off) - select unique2, x.* - from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true; - QUERY PLAN ------------------------------------------------ - Nested Loop Left Join - -> Seq Scan on int4_tbl x - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = x.f1) -(4 rows) - --- check scoping of lateral versus parent references --- the first of these should return int8_tbl.q2, the second int8_tbl.q1 -select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl; - q1 | q2 | r -------------------+-------------------+------------------- - 123 | 456 | 456 - 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | -4567890123456789 -(5 rows) - -select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl; - q1 | q2 | r -------------------+-------------------+------------------ - 123 | 456 | 123 - 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 -(5 rows) - --- lateral with function in FROM -select count(*) from tenk1 a, lateral generate_series(1,two) g; - count -------- - 5000 -(1 row) - -explain (costs off) - select count(*) from tenk1 a, lateral generate_series(1,two) g; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 a - -> Memoize - Cache Key: a.two - Cache Mode: binary - -> Function Scan on generate_series g -(7 rows) - -explain (costs off) - select count(*) from tenk1 a cross join lateral generate_series(1,two) g; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 a - -> Memoize - Cache Key: a.two - Cache Mode: binary - -> Function Scan on generate_series g -(7 rows) - --- don't need the explicit LATERAL keyword for functions -explain (costs off) - select count(*) from tenk1 a, generate_series(1,two) g; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 a - -> Memoize - Cache Key: a.two - Cache Mode: binary - -> Function Scan on generate_series g -(7 rows) - --- lateral with UNION ALL subselect -explain (costs off) - select * from generate_series(100,200) g, - lateral (select * from int8_tbl a where g = q1 union all - select * from int8_tbl b where g = q2) ss; - QUERY PLAN ------------------------------------------- - Nested Loop - -> Function Scan on generate_series g - -> Append - -> Seq Scan on int8_tbl a - Filter: (g.g = q1) - -> Seq Scan on int8_tbl b - Filter: (g.g = q2) -(7 rows) - -select * from generate_series(100,200) g, - lateral (select * from int8_tbl a where g = q1 union all - select * from int8_tbl b where g = q2) ss; - g | q1 | q2 ------+------------------+------------------ - 123 | 123 | 456 - 123 | 123 | 4567890123456789 - 123 | 4567890123456789 | 123 -(3 rows) - --- lateral with VALUES -explain (costs off) - select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Merge Join - Merge Cond: (a.unique1 = b.unique2) - -> Index Only Scan using tenk1_unique1 on tenk1 a - -> Index Only Scan using tenk1_unique2 on tenk1 b -(5 rows) - -select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x; - count -------- - 10000 -(1 row) - --- lateral with VALUES, no flattening possible -explain (costs off) - select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; - QUERY PLAN ------------------------------------------------------------------- - Aggregate - -> Nested Loop - -> Nested Loop - -> Index Only Scan using tenk1_unique1 on tenk1 a - -> Values Scan on "*VALUES*" - -> Memoize - Cache Key: "*VALUES*".column1 - Cache Mode: logical - -> Index Only Scan using tenk1_unique2 on tenk1 b - Index Cond: (unique2 = "*VALUES*".column1) -(10 rows) - -select count(*) from tenk1 a, - tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x; - count -------- - 10000 -(1 row) - --- lateral injecting a strange outer join condition -explain (costs off) - select * from int8_tbl a, - int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) - on x.q2 = ss.z - order by a.q1, a.q2, x.q1, x.q2, ss.z; - QUERY PLAN ------------------------------------------------- - Sort - Sort Key: a.q1, a.q2, x.q1, x.q2, (a.q1) - -> Nested Loop - -> Seq Scan on int8_tbl a - -> Hash Left Join - Hash Cond: (x.q2 = (a.q1)) - -> Seq Scan on int8_tbl x - -> Hash - -> Seq Scan on int4_tbl y -(9 rows) - -select * from int8_tbl a, - int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z) - on x.q2 = ss.z - order by a.q1, a.q2, x.q1, x.q2, ss.z; - q1 | q2 | q1 | q2 | z -------------------+-------------------+------------------+-------------------+------------------ - 123 | 456 | 123 | 456 | - 123 | 456 | 123 | 4567890123456789 | - 123 | 456 | 4567890123456789 | -4567890123456789 | - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 123 | 123 - 123 | 456 | 4567890123456789 | 4567890123456789 | - 123 | 4567890123456789 | 123 | 456 | - 123 | 4567890123456789 | 123 | 4567890123456789 | - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | - 4567890123456789 | -4567890123456789 | 123 | 456 | - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789 | - 4567890123456789 | -4567890123456789 | 4567890123456789 | 123 | - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | -4567890123456789 | - 4567890123456789 | 123 | 4567890123456789 | 123 | - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 456 | - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 -(57 rows) - --- lateral reference to a join alias variable -select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, - lateral (select x) ss2(y); - x | f1 | y ----+----+--- - 0 | 0 | 0 -(1 row) - -select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1, - lateral (values(x)) ss2(y); - x | f1 | y --------------+-------------+------------- - 0 | 0 | 0 - 123456 | 123456 | 123456 - -123456 | -123456 | -123456 - 2147483647 | 2147483647 | 2147483647 - -2147483647 | -2147483647 | -2147483647 -(5 rows) - -select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j, - lateral (select x) ss2(y); - x | f1 | y ----+----+--- - 0 | 0 | 0 -(1 row) - --- lateral references requiring pullup -select * from (values(1)) x(lb), - lateral generate_series(lb,4) x4; - lb | x4 -----+---- - 1 | 1 - 1 | 2 - 1 | 3 - 1 | 4 -(4 rows) - -select * from (select f1/1000000000 from int4_tbl) x(lb), - lateral generate_series(lb,4) x4; - lb | x4 -----+---- - 0 | 0 - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 0 - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 0 - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 2 | 2 - 2 | 3 - 2 | 4 - -2 | -2 - -2 | -1 - -2 | 0 - -2 | 1 - -2 | 2 - -2 | 3 - -2 | 4 -(25 rows) - -select * from (values(1)) x(lb), - lateral (values(lb)) y(lbcopy); - lb | lbcopy -----+-------- - 1 | 1 -(1 row) - -select * from (values(1)) x(lb), - lateral (select lb from int4_tbl) y(lbcopy); - lb | lbcopy -----+-------- - 1 | 1 - 1 | 1 - 1 | 1 - 1 | 1 - 1 | 1 -(5 rows) - -select * from - int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, - lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2); - q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 -------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- - 123 | 456 | | | 123 | | - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | -(10 rows) - -select * from - int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, - lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); - q1 | q2 | q1 | q2 | xq1 | yq1 | yq2 -------------------+-------------------+------------------+-------------------+------------------+------------------+------------------- - 123 | 456 | | | 123 | | - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 123 | 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 4567890123456789 | 123 | 456 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | -4567890123456789 | | | 4567890123456789 | | -(10 rows) - -select x.* from - int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1, - lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2); - q1 | q2 -------------------+------------------- - 123 | 456 - 123 | 4567890123456789 - 123 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 -(10 rows) - -select v.* from - (int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1) - left join int4_tbl z on z.f1 = x.q2, - lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); - vx | vy --------------------+------------------- - 123 | - 456 | - 123 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 123 - 123 | 456 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | - -4567890123456789 | -(20 rows) - -select v.* from - (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) - left join int4_tbl z on z.f1 = x.q2, - lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy); - vx | vy --------------------+------------------- - 4567890123456789 | 123 - 123 | 456 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | - 456 | - 4567890123456789 | - -4567890123456789 | -(20 rows) - -select v.* from - (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1) - left join int4_tbl z on z.f1 = x.q2, - lateral (select x.q1,y.q1 from onerow union all select x.q2,y.q2 from onerow) v(vx,vy); - vx | vy --------------------+------------------- - 4567890123456789 | 123 - 123 | 456 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 123 - 123 | 4567890123456789 - 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | 4567890123456789 - 4567890123456789 | -4567890123456789 - 123 | - 456 | - 4567890123456789 | - -4567890123456789 | -(20 rows) - -explain (verbose, costs off) -select * from - int8_tbl a left join - lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - Output: a.q1, a.q2, b.q1, b.q2, (a.q2) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2, a.q2 - Filter: (a.q2 = b.q1) -(7 rows) - -select * from - int8_tbl a left join - lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1; - q1 | q2 | q1 | q2 | x -------------------+-------------------+------------------+-------------------+------------------ - 123 | 456 | | | - 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | | | -(10 rows) - -explain (verbose, costs off) -select * from - int8_tbl a left join - lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; - QUERY PLAN ------------------------------------------------------------------- - Nested Loop Left Join - Output: a.q1, a.q2, b.q1, b.q2, (COALESCE(a.q2, '42'::bigint)) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2, COALESCE(a.q2, '42'::bigint) - Filter: (a.q2 = b.q1) -(7 rows) - -select * from - int8_tbl a left join - lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1; - q1 | q2 | q1 | q2 | x -------------------+-------------------+------------------+-------------------+------------------ - 123 | 456 | | | - 123 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 123 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | 123 | 123 | 456 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | -4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | | | -(10 rows) - --- lateral can result in join conditions appearing below their --- real semantic level -explain (verbose, costs off) -select * from int4_tbl i left join - lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; - QUERY PLAN -------------------------------------------- - Hash Left Join - Output: i.f1, j.f1 - Hash Cond: (i.f1 = j.f1) - -> Seq Scan on public.int4_tbl i - Output: i.f1 - -> Hash - Output: j.f1 - -> Seq Scan on public.int2_tbl j - Output: j.f1 -(9 rows) - -select * from int4_tbl i left join - lateral (select * from int2_tbl j where i.f1 = j.f1) k on true; - f1 | f1 --------------+---- - 0 | 0 - 123456 | - -123456 | - 2147483647 | - -2147483647 | -(5 rows) - -explain (verbose, costs off) -select * from int4_tbl i left join - lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - Output: i.f1, (COALESCE(i.*, i.*)) - -> Seq Scan on public.int4_tbl i - Output: i.f1, i.* - -> Seq Scan on public.int2_tbl j - Output: j.f1, COALESCE(i.*, i.*) - Filter: (i.f1 = j.f1) -(7 rows) - -select * from int4_tbl i left join - lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true; - f1 | coalesce --------------+---------- - 0 | (0) - 123456 | - -123456 | - 2147483647 | - -2147483647 | -(5 rows) - -explain (verbose, costs off) -select * from int4_tbl a, - lateral ( - select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) - ) ss; - QUERY PLAN -------------------------------------------------- - Nested Loop - Output: a.f1, b.f1, c.q1, c.q2 - -> Seq Scan on public.int4_tbl a - Output: a.f1 - -> Hash Left Join - Output: b.f1, c.q1, c.q2 - Hash Cond: (b.f1 = c.q1) - -> Seq Scan on public.int4_tbl b - Output: b.f1 - -> Hash - Output: c.q1, c.q2 - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 - Filter: (a.f1 = c.q2) -(14 rows) - -select * from int4_tbl a, - lateral ( - select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2) - ) ss; - f1 | f1 | q1 | q2 --------------+-------------+----+---- - 0 | 0 | | - 0 | 123456 | | - 0 | -123456 | | - 0 | 2147483647 | | - 0 | -2147483647 | | - 123456 | 0 | | - 123456 | 123456 | | - 123456 | -123456 | | - 123456 | 2147483647 | | - 123456 | -2147483647 | | - -123456 | 0 | | - -123456 | 123456 | | - -123456 | -123456 | | - -123456 | 2147483647 | | - -123456 | -2147483647 | | - 2147483647 | 0 | | - 2147483647 | 123456 | | - 2147483647 | -123456 | | - 2147483647 | 2147483647 | | - 2147483647 | -2147483647 | | - -2147483647 | 0 | | - -2147483647 | 123456 | | - -2147483647 | -123456 | | - -2147483647 | 2147483647 | | - -2147483647 | -2147483647 | | -(25 rows) - --- lateral reference in a PlaceHolderVar evaluated at join level -explain (verbose, costs off) -select * from - int8_tbl a left join lateral - (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from - int8_tbl b cross join int8_tbl c) ss - on a.q2 = ss.bq1; - QUERY PLAN -------------------------------------------------------------- - Nested Loop Left Join - Output: a.q1, a.q2, b.q1, c.q1, (LEAST(a.q1, b.q1, c.q1)) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Nested Loop - Output: b.q1, c.q1, LEAST(a.q1, b.q1, c.q1) - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2 - Filter: (a.q2 = b.q1) - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 -(11 rows) - -select * from - int8_tbl a left join lateral - (select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from - int8_tbl b cross join int8_tbl c) ss - on a.q2 = ss.bq1; - q1 | q2 | bq1 | cq1 | least -------------------+-------------------+------------------+------------------+------------------ - 123 | 456 | | | - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 123 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 123 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 123 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 123 | 123 | 4567890123456789 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 123 | 123 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 4567890123456789 | -4567890123456789 | | | -(42 rows) - --- case requiring nested PlaceHolderVars -explain (verbose, costs off) -select * from - int8_tbl c left join ( - int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1 - on a.q2 = ss1.q1 - cross join - lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 - ) on c.q2 = ss2.q1, - lateral (select ss2.y offset 0) ss3; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)), d.q1, (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)), ((COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2))) - -> Hash Right Join - Output: c.q1, c.q2, a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)), d.q1, (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) - Hash Cond: (d.q1 = c.q2) - -> Nested Loop - Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)), d.q1, (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) - -> Hash Left Join - Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, '42'::bigint)) - Hash Cond: (a.q2 = b.q1) - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Hash - Output: b.q1, (COALESCE(b.q2, '42'::bigint)) - -> Seq Scan on public.int8_tbl b - Output: b.q1, COALESCE(b.q2, '42'::bigint) - -> Seq Scan on public.int8_tbl d - Output: d.q1, COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2) - -> Hash - Output: c.q1, c.q2 - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 - -> Result - Output: (COALESCE((COALESCE(b.q2, '42'::bigint)), d.q2)) -(24 rows) - --- another case requiring nested PlaceHolderVars -explain (verbose, costs off) -select * from - (select 0 as val0) as ss0 - left join (select 1 as val) as ss1 on true - left join lateral (select ss1.val as val_filtered where false) as ss2 on true; - QUERY PLAN --------------------------------- - Nested Loop Left Join - Output: 0, (1), ((1)) - Join Filter: false - -> Result - Output: 1 - -> Result - Output: (1) - One-Time Filter: false -(8 rows) - -select * from - (select 0 as val0) as ss0 - left join (select 1 as val) as ss1 on true - left join lateral (select ss1.val as val_filtered where false) as ss2 on true; - val0 | val | val_filtered -------+-----+-------------- - 0 | 1 | -(1 row) - --- case that breaks the old ph_may_need optimization -explain (verbose, costs off) -select c.*,a.*,ss1.q1,ss2.q1,ss3.* from - int8_tbl c left join ( - int8_tbl a left join - (select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2 - where q1 < f1) ss1 - on a.q2 = ss1.q1 - cross join - lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2 - ) on c.q2 = ss2.q1, - lateral (select * from int4_tbl i where ss2.y > f1) ss3; - QUERY PLAN ---------------------------------------------------------------------------------------------------------- - Nested Loop - Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, i.f1 - Join Filter: ((COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2)) > i.f1) - -> Hash Right Join - Output: c.q1, c.q2, a.q1, a.q2, b.q1, d.q1, (COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2)) - Hash Cond: (d.q1 = c.q2) - -> Nested Loop - Output: a.q1, a.q2, b.q1, d.q1, (COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2)) - -> Hash Right Join - Output: a.q1, a.q2, b.q1, (COALESCE(b.q2, (b2.f1)::bigint)) - Hash Cond: (b.q1 = a.q2) - -> Nested Loop - Output: b.q1, COALESCE(b.q2, (b2.f1)::bigint) - Join Filter: (b.q1 < b2.f1) - -> Seq Scan on public.int8_tbl b - Output: b.q1, b.q2 - -> Materialize - Output: b2.f1 - -> Seq Scan on public.int4_tbl b2 - Output: b2.f1 - -> Hash - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl a - Output: a.q1, a.q2 - -> Seq Scan on public.int8_tbl d - Output: d.q1, COALESCE((COALESCE(b.q2, (b2.f1)::bigint)), d.q2) - -> Hash - Output: c.q1, c.q2 - -> Seq Scan on public.int8_tbl c - Output: c.q1, c.q2 - -> Materialize - Output: i.f1 - -> Seq Scan on public.int4_tbl i - Output: i.f1 -(34 rows) - --- check processing of postponed quals (bug #9041) -explain (verbose, costs off) -select * from - (select 1 as x offset 0) x cross join (select 2 as y offset 0) y - left join lateral ( - select * from (select 3 as z offset 0) z where z.z = x.x - ) zz on zz.z = y.y; - QUERY PLAN ----------------------------------------------- - Nested Loop Left Join - Output: (1), (2), (3) - Join Filter: (((3) = (1)) AND ((3) = (2))) - -> Nested Loop - Output: (1), (2) - -> Result - Output: 1 - -> Result - Output: 2 - -> Result - Output: 3 -(11 rows) - --- a new postponed-quals issue (bug #17768) -explain (costs off) -select * from int4_tbl t1, - lateral (select * from int4_tbl t2 inner join int4_tbl t3 on t1.f1 = 1 - inner join (int4_tbl t4 left join int4_tbl t5 on true) on true) ss; - QUERY PLAN -------------------------------------------------- - Nested Loop Left Join - -> Nested Loop - -> Nested Loop - -> Nested Loop - -> Seq Scan on int4_tbl t1 - Filter: (f1 = 1) - -> Seq Scan on int4_tbl t2 - -> Materialize - -> Seq Scan on int4_tbl t3 - -> Materialize - -> Seq Scan on int4_tbl t4 - -> Materialize - -> Seq Scan on int4_tbl t5 -(13 rows) - --- check dummy rels with lateral references (bug #15694) -explain (verbose, costs off) -select * from int8_tbl i8 left join lateral - (select *, i8.q2 from int4_tbl where false) ss on true; - QUERY PLAN ----------------------------------------------- - Nested Loop Left Join - Output: i8.q1, i8.q2, int4_tbl.f1, (i8.q2) - Join Filter: false - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Result - Output: int4_tbl.f1, i8.q2 - Replaces: Scan on int4_tbl - One-Time Filter: false -(9 rows) - -explain (verbose, costs off) -select * from int8_tbl i8 left join lateral - (select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true; - QUERY PLAN ------------------------------------------------ - Nested Loop Left Join - Output: i8.q1, i8.q2, i1.f1, i2.f1, (i8.q2) - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, i8.q2 - -> Result - Output: i1.f1, i2.f1, i8.q2 - Replaces: Join on i1, i2 - One-Time Filter: false -(8 rows) - --- check handling of nested appendrels inside LATERAL -select * from - ((select 2 as v) union all (select 3 as v)) as q1 - cross join lateral - ((select * from - ((select 4 as v) union all (select 5 as v)) as q3) - union all - (select q1.v) - ) as q2; - v | v ----+--- - 2 | 4 - 2 | 5 - 2 | 2 - 3 | 4 - 3 | 5 - 3 | 3 -(6 rows) - --- check the number of columns specified -SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d); -ERROR: join expression "ss" has 3 columns available but 4 columns specified --- check we don't try to do a unique-ified semijoin with LATERAL -explain (verbose, costs off) -select * from - (values (0,9998), (1,1000)) v(id,x), - lateral (select f1 from int4_tbl - where f1 = any (select unique1 from tenk1 - where unique2 = v.x offset 0)) ss; - QUERY PLAN ----------------------------------------------------------------------- - Nested Loop - Output: "*VALUES*".column1, "*VALUES*".column2, int4_tbl.f1 - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1, "*VALUES*".column2 - -> Nested Loop Semi Join - Output: int4_tbl.f1 - Join Filter: (int4_tbl.f1 = tenk1.unique1) - -> Seq Scan on public.int4_tbl - Output: int4_tbl.f1 - -> Materialize - Output: tenk1.unique1 - -> Index Scan using tenk1_unique2 on public.tenk1 - Output: tenk1.unique1 - Index Cond: (tenk1.unique2 = "*VALUES*".column2) -(14 rows) - -select * from - (values (0,9998), (1,1000)) v(id,x), - lateral (select f1 from int4_tbl - where f1 = any (select unique1 from tenk1 - where unique2 = v.x offset 0)) ss; - id | x | f1 -----+------+---- - 0 | 9998 | 0 -(1 row) - --- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, --- but we can make the test case much more compact with LATERAL) -explain (verbose, costs off) -select * from (values (0), (1)) v(id), -lateral (select * from int8_tbl t1, - lateral (select * from - (select * from int8_tbl t2 - where (q1, random() > 0) = any (select q2, random() > 0 from int8_tbl t3 - where q2 = (select greatest(t1.q1,t2.q2)) - and (select v.id=0)) offset 0) ss2) ss - where t1.q1 = ss.q2) ss0; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- - Nested Loop - Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 - -> Seq Scan on public.int8_tbl t1 - Output: t1.q1, t1.q2 - -> Nested Loop - Output: "*VALUES*".column1, ss2.q1, ss2.q2 - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> Subquery Scan on ss2 - Output: ss2.q1, ss2.q2 - Filter: (t1.q1 = ss2.q2) - -> Seq Scan on public.int8_tbl t2 - Output: t2.q1, t2.q2 - Filter: (ANY ((t2.q1 = (SubPlan any_1).col1) AND ((random() > '0'::double precision) = (SubPlan any_1).col2))) - SubPlan any_1 - -> Result - Output: t3.q2, (random() > '0'::double precision) - One-Time Filter: (InitPlan expr_2).col1 - InitPlan expr_1 - -> Result - Output: GREATEST(t1.q1, t2.q2) - InitPlan expr_2 - -> Result - Output: ("*VALUES*".column1 = 0) - -> Seq Scan on public.int8_tbl t3 - Output: t3.q1, t3.q2 - Filter: (t3.q2 = (InitPlan expr_1).col1) -(27 rows) - -select * from (values (0), (1)) v(id), -lateral (select * from int8_tbl t1, - lateral (select * from - (select * from int8_tbl t2 - where (q1, random() > 0) = any (select q2, random() > 0 from int8_tbl t3 - where q2 = (select greatest(t1.q1,t2.q2)) - and (select v.id=0)) offset 0) ss2) ss - where t1.q1 = ss.q2) ss0; - id | q1 | q2 | q1 | q2 -----+------------------+-------------------+------------------+------------------ - 0 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 - 0 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 - 0 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 -(3 rows) - --- test some error cases where LATERAL should have been used but wasn't -select f1,g from int4_tbl a, (select f1 as g) ss; -ERROR: column "f1" does not exist -LINE 1: select f1,g from int4_tbl a, (select f1 as g) ss; - ^ -DETAIL: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. -HINT: To reference that column, you must mark this subquery with LATERAL. -select f1,g from int4_tbl a, (select a.f1 as g) ss; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: select f1,g from int4_tbl a, (select a.f1 as g) ss; - ^ -DETAIL: There is an entry for table "a", but it cannot be referenced from this part of the query. -HINT: To reference that table, you must mark this subquery with LATERAL. -select f1,g from int4_tbl a cross join (select f1 as g) ss; -ERROR: column "f1" does not exist -LINE 1: select f1,g from int4_tbl a cross join (select f1 as g) ss; - ^ -DETAIL: There is a column named "f1" in table "a", but it cannot be referenced from this part of the query. -HINT: To reference that column, you must mark this subquery with LATERAL. -select f1,g from int4_tbl a cross join (select a.f1 as g) ss; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: select f1,g from int4_tbl a cross join (select a.f1 as g) ss... - ^ -DETAIL: There is an entry for table "a", but it cannot be referenced from this part of the query. -HINT: To reference that table, you must mark this subquery with LATERAL. --- SQL:2008 says the left table is in scope but illegal to access here -select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: ... int4_tbl a right join lateral generate_series(0, a.f1) g on... - ^ -DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. -select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true; -ERROR: invalid reference to FROM-clause entry for table "a" -LINE 1: ...m int4_tbl a full join lateral generate_series(0, a.f1) g on... - ^ -DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. --- check we complain about ambiguous table references -select * from - int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss); -ERROR: table reference "x" is ambiguous -LINE 2: ...cross join (int4_tbl x cross join lateral (select x.f1) ss); - ^ --- LATERAL can be used to put an aggregate into the FROM clause of its query -select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 1: select 1 from tenk1 a, lateral (select max(a.unique1) from i... - ^ --- check behavior of LATERAL in UPDATE/DELETE -create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl; --- error, can't do this: -update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; -ERROR: column "x1" does not exist -LINE 1: ... set x2 = f1 from (select * from int4_tbl where f1 = x1) ss; - ^ -DETAIL: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. -update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...t x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss... - ^ -DETAIL: There is an entry for table "xx1", but it cannot be referenced from this part of the query. --- can't do it even with LATERAL: -update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...= f1 from lateral (select * from int4_tbl where f1 = x1) ss; - ^ -HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. --- we might in future allow something like this, but for now it's an error: -update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss; -ERROR: table name "xx1" specified more than once --- also errors: -delete from xx1 using (select * from int4_tbl where f1 = x1) ss; -ERROR: column "x1" does not exist -LINE 1: ...te from xx1 using (select * from int4_tbl where f1 = x1) ss; - ^ -DETAIL: There is a column named "x1" in table "xx1", but it cannot be referenced from this part of the query. -delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss... - ^ -DETAIL: There is an entry for table "xx1", but it cannot be referenced from this part of the query. -delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss; -ERROR: invalid reference to FROM-clause entry for table "xx1" -LINE 1: ...xx1 using lateral (select * from int4_tbl where f1 = x1) ss; - ^ -HINT: There is an entry for table "xx1", but it cannot be referenced from this part of the query. --- --- test LATERAL reference propagation down a multi-level inheritance hierarchy --- produced for a multi-level partitioned table hierarchy. --- -create table join_pt1 (a int, b int, c varchar) partition by range(a); -create table join_pt1p1 partition of join_pt1 for values from (0) to (100) partition by range(b); -create table join_pt1p2 partition of join_pt1 for values from (100) to (200); -create table join_pt1p1p1 partition of join_pt1p1 for values from (0) to (100); -insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y'); -create table join_ut1 (a int, b int, c varchar); -insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z'); -explain (verbose, costs off) -select t1.b, ss.phv from join_ut1 t1 left join lateral - (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv - from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss - on t1.a = ss.t2a order by t1.a; - QUERY PLAN --------------------------------------------------------------------- - Sort - Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a - Sort Key: t1.a - -> Nested Loop Left Join - Output: t1.b, (LEAST(t1.a, t2.a, t3.a)), t1.a - -> Seq Scan on public.join_ut1 t1 - Output: t1.a, t1.b, t1.c - -> Hash Join - Output: t2.a, LEAST(t1.a, t2.a, t3.a) - Hash Cond: (t3.b = t2.a) - -> Seq Scan on public.join_ut1 t3 - Output: t3.a, t3.b, t3.c - -> Hash - Output: t2.a - -> Append - -> Seq Scan on public.join_pt1p1p1 t2_1 - Output: t2_1.a - Filter: (t1.a = t2_1.a) - -> Seq Scan on public.join_pt1p2 t2_2 - Output: t2_2.a - Filter: (t1.a = t2_2.a) -(21 rows) - -select t1.b, ss.phv from join_ut1 t1 left join lateral - (select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv - from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss - on t1.a = ss.t2a order by t1.a; - b | phv ------+----- - 2 | - 101 | 101 -(2 rows) - -drop table join_pt1; -drop table join_ut1; --- --- test estimation behavior with multi-column foreign key and constant qual --- -begin; -create table fkest (x integer, x10 integer, x10b integer, x100 integer); -insert into fkest select x, x/10, x/10, x/100 from generate_series(1,1000) x; -create unique index on fkest(x, x10, x100); -analyze fkest; -explain (costs off) -select * from fkest f1 - join fkest f2 on (f1.x = f2.x and f1.x10 = f2.x10b and f1.x100 = f2.x100) - join fkest f3 on f1.x = f3.x - where f1.x100 = 2; - QUERY PLAN ------------------------------------------------------------ - Nested Loop - -> Hash Join - Hash Cond: ((f2.x = f1.x) AND (f2.x10b = f1.x10)) - -> Seq Scan on fkest f2 - Filter: (x100 = 2) - -> Hash - -> Seq Scan on fkest f1 - Filter: (x100 = 2) - -> Index Scan using fkest_x_x10_x100_idx on fkest f3 - Index Cond: (x = f1.x) -(10 rows) - -alter table fkest add constraint fk - foreign key (x, x10b, x100) references fkest (x, x10, x100); -explain (costs off) -select * from fkest f1 - join fkest f2 on (f1.x = f2.x and f1.x10 = f2.x10b and f1.x100 = f2.x100) - join fkest f3 on f1.x = f3.x - where f1.x100 = 2; - QUERY PLAN ------------------------------------------------------ - Hash Join - Hash Cond: ((f2.x = f1.x) AND (f2.x10b = f1.x10)) - -> Hash Join - Hash Cond: (f3.x = f2.x) - -> Seq Scan on fkest f3 - -> Hash - -> Seq Scan on fkest f2 - Filter: (x100 = 2) - -> Hash - -> Seq Scan on fkest f1 - Filter: (x100 = 2) -(11 rows) - -rollback; --- --- test that foreign key join estimation performs sanely for outer joins --- -begin; -create table fkest (a int, b int, c int unique, primary key(a,b)); -create table fkest1 (a int, b int, primary key(a,b)); -insert into fkest select x/10, x%10, x from generate_series(1,1000) x; -insert into fkest1 select x/10, x%10 from generate_series(1,1000) x; -alter table fkest1 - add constraint fkest1_a_b_fkey foreign key (a,b) references fkest; -analyze fkest; -analyze fkest1; -explain (costs off) -select * -from fkest f - left join fkest1 f1 on f.a = f1.a and f.b = f1.b - left join fkest1 f2 on f.a = f2.a and f.b = f2.b - left join fkest1 f3 on f.a = f3.a and f.b = f3.b -where f.c = 1; - QUERY PLAN ------------------------------------------------------------------- - Nested Loop Left Join - -> Nested Loop Left Join - -> Nested Loop Left Join - -> Index Scan using fkest_c_key on fkest f - Index Cond: (c = 1) - -> Index Only Scan using fkest1_pkey on fkest1 f1 - Index Cond: ((a = f.a) AND (b = f.b)) - -> Index Only Scan using fkest1_pkey on fkest1 f2 - Index Cond: ((a = f.a) AND (b = f.b)) - -> Index Only Scan using fkest1_pkey on fkest1 f3 - Index Cond: ((a = f.a) AND (b = f.b)) -(11 rows) - -rollback; --- --- test planner's ability to mark joins as unique --- -create table j1 (id int primary key); -create table j2 (id int primary key); -create table j3 (id int); -insert into j1 values(1),(2),(3); -insert into j2 values(1),(2),(3); -insert into j3 values(1),(1); -analyze j1; -analyze j2; -analyze j3; --- ensure join is properly marked as unique -explain (verbose, costs off) -select * from j1 inner join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- ensure join is not unique when not an equi-join -explain (verbose, costs off) -select * from j1 inner join j2 on j1.id > j2.id; - QUERY PLAN ------------------------------------ - Nested Loop - Output: j1.id, j2.id - Join Filter: (j1.id > j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Materialize - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(9 rows) - --- ensure non-unique rel is not chosen as inner -explain (verbose, costs off) -select * from j1 inner join j3 on j1.id = j3.id; - QUERY PLAN ------------------------------------ - Hash Join - Output: j1.id, j3.id - Inner Unique: true - Hash Cond: (j3.id = j1.id) - -> Seq Scan on public.j3 - Output: j3.id - -> Hash - Output: j1.id - -> Seq Scan on public.j1 - Output: j1.id -(10 rows) - --- ensure left join is marked as unique -explain (verbose, costs off) -select * from j1 left join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Left Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- ensure right join is marked as unique -explain (verbose, costs off) -select * from j1 right join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Left Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j2.id = j1.id) - -> Seq Scan on public.j2 - Output: j2.id - -> Hash - Output: j1.id - -> Seq Scan on public.j1 - Output: j1.id -(10 rows) - --- ensure full join is marked as unique -explain (verbose, costs off) -select * from j1 full join j2 on j1.id = j2.id; - QUERY PLAN ------------------------------------ - Hash Full Join - Output: j1.id, j2.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- a clauseless (cross) join can't be unique -explain (verbose, costs off) -select * from j1 cross join j2; - QUERY PLAN ------------------------------------ - Nested Loop - Output: j1.id, j2.id - -> Seq Scan on public.j1 - Output: j1.id - -> Materialize - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(8 rows) - --- ensure a natural join is marked as unique -explain (verbose, costs off) -select * from j1 natural join j2; - QUERY PLAN ------------------------------------ - Hash Join - Output: j1.id - Inner Unique: true - Hash Cond: (j1.id = j2.id) - -> Seq Scan on public.j1 - Output: j1.id - -> Hash - Output: j2.id - -> Seq Scan on public.j2 - Output: j2.id -(10 rows) - --- ensure a distinct clause allows the inner to become unique -explain (verbose, costs off) -select * from j1 -inner join (select distinct id from j3) j3 on j1.id = j3.id; - QUERY PLAN ------------------------------------------ - Nested Loop - Output: j1.id, j3.id - Inner Unique: true - Join Filter: (j1.id = j3.id) - -> Unique - Output: j3.id - -> Sort - Output: j3.id - Sort Key: j3.id - -> Seq Scan on public.j3 - Output: j3.id - -> Seq Scan on public.j1 - Output: j1.id -(13 rows) - --- ensure group by clause allows the inner to become unique -explain (verbose, costs off) -select * from j1 -inner join (select id from j3 group by id) j3 on j1.id = j3.id; - QUERY PLAN ------------------------------------------ - Nested Loop - Output: j1.id, j3.id - Inner Unique: true - Join Filter: (j1.id = j3.id) - -> Group - Output: j3.id - Group Key: j3.id - -> Sort - Output: j3.id - Sort Key: j3.id - -> Seq Scan on public.j3 - Output: j3.id - -> Seq Scan on public.j1 - Output: j1.id -(14 rows) - -drop table j1; -drop table j2; -drop table j3; --- test more complex permutations of unique joins -create table j1 (id1 int, id2 int, primary key(id1,id2)); -create table j2 (id1 int, id2 int, primary key(id1,id2)); -create table j3 (id1 int, id2 int, primary key(id1,id2)); -insert into j1 values(1,1),(1,2); -insert into j2 values(1,1); -insert into j3 values(1,1); -analyze j1; -analyze j2; -analyze j3; --- ensure there's no unique join when not all columns which are part of the --- unique index are seen in the join clause -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id1 = j2.id1; - QUERY PLAN ------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j1.id1 = j2.id1) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 -(7 rows) - --- ensure proper unique detection with multiple join quals -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2; - QUERY PLAN ----------------------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Inner Unique: true - Join Filter: ((j1.id1 = j2.id1) AND (j1.id2 = j2.id2)) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 -(8 rows) - --- ensure we don't detect the join to be unique when quals are not part of the --- join condition -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id1 = j2.id1 where j1.id2 = 1; - QUERY PLAN ------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j1.id1 = j2.id1) - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 - Filter: (j1.id2 = 1) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 -(8 rows) - --- as above, but for left joins. -explain (verbose, costs off) -select * from j1 -left join j2 on j1.id1 = j2.id1 where j1.id2 = 1; - QUERY PLAN ------------------------------------------- - Nested Loop Left Join - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j1.id1 = j2.id1) - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 - Filter: (j1.id2 = 1) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 -(8 rows) - -create unique index j1_id2_idx on j1(id2) where id2 > 0; --- ensure we don't use a partial unique index as unique proofs -explain (verbose, costs off) -select * from j1 -inner join j2 on j1.id2 = j2.id2; - QUERY PLAN ------------------------------------------- - Nested Loop - Output: j1.id1, j1.id2, j2.id1, j2.id2 - Join Filter: (j2.id2 = j1.id2) - -> Seq Scan on public.j2 - Output: j2.id1, j2.id2 - -> Seq Scan on public.j1 - Output: j1.id1, j1.id2 -(7 rows) - -drop index j1_id2_idx; --- validate logic in merge joins which skips mark and restore. --- it should only do this if all quals which were used to detect the unique --- are present as join quals, and not plain quals. -set enable_nestloop to 0; -set enable_hashjoin to 0; -set enable_sort to 0; --- create indexes that will be preferred over the PKs to perform the join -create index j1_id1_idx on j1 (id1) where id1 % 1000 = 1; -create index j2_id1_idx on j2 (id1) where id1 % 1000 = 1; --- need an additional row in j2, if we want j2_id1_idx to be preferred -insert into j2 values(1,2); -analyze j2; -explain (costs off) select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1; - QUERY PLAN ------------------------------------------ - Merge Join - Merge Cond: (j1.id1 = j2.id1) - Join Filter: (j2.id2 = j1.id2) - -> Index Scan using j1_id1_idx on j1 - -> Index Scan using j2_id1_idx on j2 -(5 rows) - -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1; - id1 | id2 | id1 | id2 ------+-----+-----+----- - 1 | 1 | 1 | 1 - 1 | 2 | 1 | 2 -(2 rows) - --- Exercise array keys mark/restore B-Tree code -explain (costs off) select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 = any (array[1]); - QUERY PLAN ----------------------------------------------------- - Merge Join - Merge Cond: (j1.id1 = j2.id1) - Join Filter: (j2.id2 = j1.id2) - -> Index Scan using j1_id1_idx on j1 - -> Index Scan using j2_id1_idx on j2 - Index Cond: (id1 = ANY ('{1}'::integer[])) -(6 rows) - -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 = any (array[1]); - id1 | id2 | id1 | id2 ------+-----+-----+----- - 1 | 1 | 1 | 1 - 1 | 2 | 1 | 2 -(2 rows) - --- Exercise array keys "find extreme element" B-Tree code -explain (costs off) select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]); - QUERY PLAN -------------------------------------------------------- - Merge Join - Merge Cond: (j1.id1 = j2.id1) - Join Filter: (j2.id2 = j1.id2) - -> Index Scan using j1_id1_idx on j1 - -> Index Scan using j2_id1_idx on j2 - Index Cond: (id1 >= ANY ('{1,5}'::integer[])) -(6 rows) - -select * from j1 -inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2 -where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]); - id1 | id2 | id1 | id2 ------+-----+-----+----- - 1 | 1 | 1 | 1 - 1 | 2 | 1 | 2 -(2 rows) - -reset enable_nestloop; -reset enable_hashjoin; -reset enable_sort; -drop table j1; -drop table j2; -drop table j3; --- check that semijoin inner is not seen as unique for a portion of the outerrel -explain (verbose, costs off) -select t1.unique1, t2.hundred -from onek t1, tenk1 t2 -where exists (select 1 from tenk1 t3 - where t3.thousand = t1.unique1 and t3.tenthous = t2.hundred) - and t1.unique1 < 1; - QUERY PLAN ---------------------------------------------------------------------------------- - Nested Loop - Output: t1.unique1, t2.hundred - -> Merge Join - Output: t1.unique1, t3.tenthous - Merge Cond: (t3.thousand = t1.unique1) - -> Unique - Output: t3.thousand, t3.tenthous - -> Index Only Scan using tenk1_thous_tenthous on public.tenk1 t3 - Output: t3.thousand, t3.tenthous - -> Index Only Scan using onek_unique1 on public.onek t1 - Output: t1.unique1 - Index Cond: (t1.unique1 < 1) - -> Index Only Scan using tenk1_hundred on public.tenk1 t2 - Output: t2.hundred - Index Cond: (t2.hundred = t3.tenthous) -(15 rows) - --- ... unless it actually is unique -create table j3 as select unique1, tenthous from onek; -vacuum analyze j3; -create unique index on j3(unique1, tenthous); -explain (verbose, costs off) -select t1.unique1, t2.hundred -from onek t1, tenk1 t2 -where exists (select 1 from j3 - where j3.unique1 = t1.unique1 and j3.tenthous = t2.hundred) - and t1.unique1 < 1; - QUERY PLAN ------------------------------------------------------------------------- - Nested Loop - Output: t1.unique1, t2.hundred - -> Nested Loop - Output: t1.unique1, j3.tenthous - -> Index Only Scan using onek_unique1 on public.onek t1 - Output: t1.unique1 - Index Cond: (t1.unique1 < 1) - -> Index Only Scan using j3_unique1_tenthous_idx on public.j3 - Output: j3.unique1, j3.tenthous - Index Cond: (j3.unique1 = t1.unique1) - -> Index Only Scan using tenk1_hundred on public.tenk1 t2 - Output: t2.hundred - Index Cond: (t2.hundred = j3.tenthous) -(13 rows) - -drop table j3; --- Exercise the "skip fetch" Bitmap Heap Scan optimization when candidate --- tuples are discarded. This may occur when: --- 1. A join doesn't require all inner tuples to be scanned for each outer --- tuple, and --- 2. The inner side is scanned using a bitmap heap scan, and --- 3. The bitmap heap scan is eligible for the "skip fetch" optimization. --- This optimization is usable when no data from the underlying table is --- needed. Use a temp table so it is only visible to this backend and --- vacuum may reliably mark all blocks in the table all visible in the --- visibility map. -CREATE TEMP TABLE skip_fetch (a INT, b INT) WITH (fillfactor=10); -INSERT INTO skip_fetch SELECT i % 3, i FROM generate_series(0,30) i; -CREATE INDEX ON skip_fetch(a); -VACUUM (ANALYZE) skip_fetch; -SET enable_indexonlyscan = off; -SET enable_seqscan = off; -EXPLAIN (COSTS OFF) -SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS NULL; - QUERY PLAN ---------------------------------------------------------- - Nested Loop Anti Join - -> Seq Scan on skip_fetch t1 - Disabled: true - -> Materialize - -> Bitmap Heap Scan on skip_fetch t2 - Recheck Cond: (a = 1) - -> Bitmap Index Scan on skip_fetch_a_idx - Index Cond: (a = 1) -(8 rows) - -SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS NULL; - a ---- -(0 rows) - -RESET enable_indexonlyscan; -RESET enable_seqscan; --- Test BitmapHeapScan with a rescan releases resources correctly -SET enable_seqscan = off; -SET enable_indexscan = off; -CREATE TEMP TABLE rescan_bhs (a INT); -INSERT INTO rescan_bhs VALUES (1), (2); -CREATE INDEX ON rescan_bhs (a); -EXPLAIN (COSTS OFF) -SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN - (SELECT a FROM rescan_bhs t3 WHERE t2.a > 1); - QUERY PLAN ------------------------------------------------------------ - Nested Loop Left Join - Join Filter: (ANY (t1.a = (SubPlan any_1).col1)) - -> Bitmap Heap Scan on rescan_bhs t1 - -> Bitmap Index Scan on rescan_bhs_a_idx - -> Materialize - -> Bitmap Heap Scan on rescan_bhs t2 - -> Bitmap Index Scan on rescan_bhs_a_idx - SubPlan any_1 - -> Result - One-Time Filter: (t2.a > 1) - -> Bitmap Heap Scan on rescan_bhs t3 - -> Bitmap Index Scan on rescan_bhs_a_idx -(12 rows) - -SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN - (SELECT a FROM rescan_bhs t3 WHERE t2.a > 1); - a | a ----+--- - 1 | 2 - 2 | 2 -(2 rows) - -RESET enable_seqscan; -RESET enable_indexscan; --- Test that we do not account for nullingrels when looking up statistics -CREATE TABLE group_tbl (a INT, b INT); -INSERT INTO group_tbl SELECT 1, 1; -CREATE STATISTICS group_tbl_stat (ndistinct) ON a, b FROM group_tbl; -ANALYZE group_tbl; -EXPLAIN (COSTS OFF) -SELECT 1 FROM group_tbl t1 - LEFT JOIN (SELECT a c1, COALESCE(a, a) c2 FROM group_tbl t2) s ON TRUE -GROUP BY s.c1, s.c2; - QUERY PLAN ------------------------------------------------- - Group - Group Key: t2.a, (COALESCE(t2.a, t2.a)) - -> Sort - Sort Key: t2.a, (COALESCE(t2.a, t2.a)) - -> Nested Loop Left Join - -> Seq Scan on group_tbl t1 - -> Seq Scan on group_tbl t2 -(7 rows) - -DROP TABLE group_tbl; --- Test that we ignore PlaceHolderVars when looking up statistics -EXPLAIN (COSTS OFF) -SELECT t1.unique1 FROM tenk1 t1 LEFT JOIN - (SELECT *, 42 AS phv FROM tenk1 t2) ss ON t1.unique2 = ss.unique2 -WHERE ss.unique1 = ss.phv AND t1.unique1 < 100; - QUERY PLAN --------------------------------------------------- - Nested Loop - -> Seq Scan on tenk1 t2 - Filter: (unique1 = 42) - -> Index Scan using tenk1_unique2 on tenk1 t1 - Index Cond: (unique2 = t2.unique2) - Filter: (unique1 < 100) -(6 rows) - -SELECT t1.unique1 FROM tenk1 t1 LEFT JOIN - (SELECT *, 42 AS phv FROM tenk1 t2) ss ON t1.unique2 = ss.unique2 -WHERE ss.unique1 = ss.phv AND t1.unique1 < 100; - unique1 ---------- - 42 -(1 row) - --- --- Test for a nested loop join involving index scan, transforming OR-clauses --- to SAOP. --- -EXPLAIN (COSTS OFF) -SELECT COUNT(*) FROM tenk1 t1, tenk1 t2 -WHERE t2.thousand = t1.tenthous OR t2.thousand = t1.unique1 OR t2.thousand = t1.unique2; - QUERY PLAN ------------------------------------------------------------------------------------------ - Aggregate - -> Nested Loop - -> Seq Scan on tenk1 t1 - -> Index Only Scan using tenk1_thous_tenthous on tenk1 t2 - Index Cond: (thousand = ANY (ARRAY[t1.tenthous, t1.unique1, t1.unique2])) -(5 rows) - -SELECT COUNT(*) FROM tenk1 t1, tenk1 t2 -WHERE t2.thousand = t1.tenthous OR t2.thousand = t1.unique1 OR t2.thousand = t1.unique2; - count -------- - 20000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT COUNT(*) FROM onek t1 LEFT JOIN tenk1 t2 - ON (t2.thousand = t1.tenthous OR t2.thousand = t1.thousand); - QUERY PLAN ------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - -> Seq Scan on onek t1 - -> Index Only Scan using tenk1_thous_tenthous on tenk1 t2 - Index Cond: (thousand = ANY (ARRAY[t1.tenthous, t1.thousand])) -(5 rows) - -SELECT COUNT(*) FROM onek t1 LEFT JOIN tenk1 t2 - ON (t2.thousand = t1.tenthous OR t2.thousand = t1.thousand); - count -------- - 19000 -(1 row) - +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/aggregates.out /tmp/cirrus-ci-build/src/test/regress/results/aggregates.out --- /tmp/cirrus-ci-build/src/test/regress/expected/aggregates.out 2026-03-09 20:33:45.124311569 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/aggregates.out 2026-03-09 20:39:11.324088603 +0000 @@ -3832,93 +3832,8 @@ create table agg_group_1 as select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 from agg_data_20k group by g%10000; -create table agg_group_2 as -select * from - (values (100), (300), (500)) as r(a), - lateral ( - select (g/2)::numeric as c1, - array_agg(g::numeric) as c2, - count(*) as c3 - from agg_data_2k - where g < r.a - group by g/2) as s; -set jit_above_cost to default; -create table agg_group_3 as -select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 - from agg_data_2k group by g/2; -create table agg_group_4 as -select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 - from agg_data_2k group by g/2; --- Produce results with hash aggregation -set enable_hashagg = true; -set enable_sort = false; -set jit_above_cost = 0; -explain (costs off) -select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 - from agg_data_20k group by g%10000; - QUERY PLAN --------------------------------- - HashAggregate - Group Key: (g % 10000) - -> Seq Scan on agg_data_20k -(3 rows) - -create table agg_hash_1 as -select g%10000 as c1, sum(g::numeric) as c2, count(*) as c3 - from agg_data_20k group by g%10000; -create table agg_hash_2 as -select * from - (values (100), (300), (500)) as r(a), - lateral ( - select (g/2)::numeric as c1, - array_agg(g::numeric) as c2, - count(*) as c3 - from agg_data_2k - where g < r.a - group by g/2) as s; -set jit_above_cost to default; -create table agg_hash_3 as -select (g/2)::numeric as c1, sum(7::int4) as c2, count(*) as c3 - from agg_data_2k group by g/2; -create table agg_hash_4 as -select (g/2)::numeric as c1, array_agg(g::numeric) as c2, count(*) as c3 - from agg_data_2k group by g/2; -set enable_sort = true; -set work_mem to default; --- Compare group aggregation results to hash aggregation results -(select * from agg_hash_1 except select * from agg_group_1) - union all -(select * from agg_group_1 except select * from agg_hash_1); - c1 | c2 | c3 -----+----+---- -(0 rows) - -(select * from agg_hash_2 except select * from agg_group_2) - union all -(select * from agg_group_2 except select * from agg_hash_2); - a | c1 | c2 | c3 ----+----+----+---- -(0 rows) - -(select * from agg_hash_3 except select * from agg_group_3) - union all -(select * from agg_group_3 except select * from agg_hash_3); - c1 | c2 | c3 -----+----+---- -(0 rows) - -(select * from agg_hash_4 except select * from agg_group_4) - union all -(select * from agg_group_4 except select * from agg_hash_4); - c1 | c2 | c3 -----+----+---- -(0 rows) - -drop table agg_group_1; -drop table agg_group_2; -drop table agg_group_3; -drop table agg_group_4; -drop table agg_hash_1; -drop table agg_hash_2; -drop table agg_hash_3; -drop table agg_hash_4; +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/update.out /tmp/cirrus-ci-build/src/test/regress/results/update.out --- /tmp/cirrus-ci-build/src/test/regress/expected/update.out 2026-03-09 20:33:45.236310237 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/update.out 2026-03-09 20:39:08.460088888 +0000 @@ -440,595 +440,8 @@ CREATE VIEW upview AS SELECT * FROM range_parted WHERE (select c > c1 FROM mintab) WITH CHECK OPTION; -- ok UPDATE upview set c = 199 WHERE b = 4; --- fail, check option violation -UPDATE upview set c = 120 WHERE b = 4; -ERROR: new row violates check option for view "upview" -DETAIL: Failing row contains (a, 4, 120, 1, 1). --- fail, row movement with check option violation -UPDATE upview set a = 'b', b = 15, c = 120 WHERE b = 4; -ERROR: new row violates check option for view "upview" -DETAIL: Failing row contains (b, 15, 120, 1, 1). --- ok, row movement, check option passes -UPDATE upview set a = 'b', b = 15 WHERE b = 4; -:show_data; - partname | a | b | c | d | e ----------------+---+----+-----+---+--- - part_a_1_a_10 | a | 1 | 1 | 1 | 1 - part_b_1_b_10 | b | 7 | 117 | 2 | 2 - part_b_1_b_10 | b | 9 | 125 | 6 | 6 - part_d_1_15 | b | 11 | 125 | 9 | 9 - part_d_1_15 | b | 12 | 116 | 1 | 1 - part_d_1_15 | b | 15 | 199 | 1 | 1 -(6 rows) - --- cleanup -DROP VIEW upview; --- RETURNING having whole-row vars. -:init_range_parted; -UPDATE range_parted set c = 95 WHERE a = 'b' and b > 10 and c > 100 returning (range_parted), *; - range_parted | a | b | c | d | e ----------------+---+----+----+----+--- - (b,15,95,16,) | b | 15 | 95 | 16 | - (b,17,95,19,) | b | 17 | 95 | 19 | -(2 rows) - -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_c_1_100 | b | 15 | 95 | 16 | - part_c_1_100 | b | 17 | 95 | 19 | -(6 rows) - --- Transition tables with update row movement -:init_range_parted; -CREATE FUNCTION trans_updatetrigfunc() RETURNS trigger LANGUAGE plpgsql AS -$$ - begin - raise notice 'trigger = %, old table = %, new table = %', - TG_NAME, - (select string_agg(old_table::text, ', ' ORDER BY a) FROM old_table), - (select string_agg(new_table::text, ', ' ORDER BY a) FROM new_table); - return null; - end; -$$; -CREATE TRIGGER trans_updatetrig - AFTER UPDATE ON range_parted REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table - FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); -UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end ) WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,12,96,1,), (b,13,97,2,), (b,15,105,16,), (b,17,105,19,), new table = (b,12,110,1,), (b,13,98,2,), (b,15,106,16,), (b,17,106,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 13 | 98 | 2 | - part_d_15_20 | b | 15 | 106 | 16 | - part_d_15_20 | b | 17 | 106 | 19 | - part_d_1_15 | b | 12 | 110 | 1 | -(6 rows) - -:init_range_parted; --- Enabling OLD TABLE capture for both DELETE as well as UPDATE stmt triggers --- should not cause DELETEd rows to be captured twice. Similar thing for --- INSERT triggers and inserted rows. -CREATE TRIGGER trans_deletetrig - AFTER DELETE ON range_parted REFERENCING OLD TABLE AS old_table - FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); -CREATE TRIGGER trans_inserttrig - AFTER INSERT ON range_parted REFERENCING NEW TABLE AS new_table - FOR EACH STATEMENT EXECUTE PROCEDURE trans_updatetrigfunc(); -UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,12,96,1,), (b,13,97,2,), (b,15,105,16,), (b,17,105,19,), new table = (b,12,146,1,), (b,13,147,2,), (b,15,155,16,), (b,17,155,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_d_15_20 | b | 15 | 155 | 16 | - part_d_15_20 | b | 17 | 155 | 19 | - part_d_1_15 | b | 12 | 146 | 1 | - part_d_1_15 | b | 13 | 147 | 2 | -(6 rows) - -DROP TRIGGER trans_deletetrig ON range_parted; -DROP TRIGGER trans_inserttrig ON range_parted; --- Don't drop trans_updatetrig yet. It is required below. --- Test with transition tuple conversion happening for rows moved into the --- new partition. This requires a trigger that references transition table --- (we already have trans_updatetrig). For inserted rows, the conversion --- is not usually needed, because the original tuple is already compatible with --- the desired transition tuple format. But conversion happens when there is a --- BR trigger because the trigger can change the inserted row. So install a --- BR triggers on those child partitions where the rows will be moved. -CREATE FUNCTION func_parted_mod_b() RETURNS trigger AS $$ -BEGIN - NEW.b = NEW.b + 1; - return NEW; -END $$ language plpgsql; -CREATE TRIGGER trig_c1_100 BEFORE UPDATE OR INSERT ON part_c_1_100 - FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); -CREATE TRIGGER trig_d1_15 BEFORE UPDATE OR INSERT ON part_d_1_15 - FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); -CREATE TRIGGER trig_d15_20 BEFORE UPDATE OR INSERT ON part_d_15_20 - FOR EACH ROW EXECUTE PROCEDURE func_parted_mod_b(); -:init_range_parted; -UPDATE range_parted set c = (case when c = 96 then 110 else c + 1 end) WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,13,96,1,), (b,14,97,2,), (b,16,105,16,), (b,18,105,19,), new table = (b,15,110,1,), (b,15,98,2,), (b,17,106,16,), (b,19,106,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 15 | 98 | 2 | - part_d_15_20 | b | 17 | 106 | 16 | - part_d_15_20 | b | 19 | 106 | 19 | - part_d_1_15 | b | 15 | 110 | 1 | -(6 rows) - -:init_range_parted; -UPDATE range_parted set c = c + 50 WHERE a = 'b' and b > 10 and c >= 96; -NOTICE: trigger = trans_updatetrig, old table = (b,13,96,1,), (b,14,97,2,), (b,16,105,16,), (b,18,105,19,), new table = (b,15,146,1,), (b,16,147,2,), (b,17,155,16,), (b,19,155,19,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_d_15_20 | b | 17 | 155 | 16 | - part_d_15_20 | b | 19 | 155 | 19 | - part_d_1_15 | b | 15 | 146 | 1 | - part_d_1_15 | b | 16 | 147 | 2 | -(6 rows) - --- Case where per-partition tuple conversion map array is allocated, but the --- map is not required for the particular tuple that is routed, thanks to --- matching table attributes of the partition and the target table. -:init_range_parted; -UPDATE range_parted set b = 15 WHERE b = 1; -NOTICE: trigger = trans_updatetrig, old table = (a,1,1,1,), new table = (a,15,1,1,) -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_10_a_20 | a | 15 | 1 | 1 | - part_c_1_100 | b | 13 | 96 | 1 | - part_c_1_100 | b | 14 | 97 | 2 | - part_d_15_20 | b | 16 | 105 | 16 | - part_d_15_20 | b | 18 | 105 | 19 | -(6 rows) - -DROP TRIGGER trans_updatetrig ON range_parted; -DROP TRIGGER trig_c1_100 ON part_c_1_100; -DROP TRIGGER trig_d1_15 ON part_d_1_15; -DROP TRIGGER trig_d15_20 ON part_d_15_20; -DROP FUNCTION func_parted_mod_b(); --- RLS policies with update-row-movement ------------------------------------------ -ALTER TABLE range_parted ENABLE ROW LEVEL SECURITY; -CREATE USER regress_range_parted_user; -GRANT ALL ON range_parted, mintab TO regress_range_parted_user; -CREATE POLICY seeall ON range_parted AS PERMISSIVE FOR SELECT USING (true); -CREATE POLICY policy_range_parted ON range_parted for UPDATE USING (true) WITH CHECK (c % 2 = 0); -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- This should fail with RLS violation error while moving row from --- part_a_10_a_20 to part_d_1_15, because we are setting 'c' to an odd number. -UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy for table "range_parted" -RESET SESSION AUTHORIZATION; --- Create a trigger on part_d_1_15 -CREATE FUNCTION func_d_1_15() RETURNS trigger AS $$ -BEGIN - NEW.c = NEW.c + 1; -- Make even numbers odd, or vice versa - return NEW; -END $$ LANGUAGE plpgsql; -CREATE TRIGGER trig_d_1_15 BEFORE INSERT ON part_d_1_15 - FOR EACH ROW EXECUTE PROCEDURE func_d_1_15(); -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- Here, RLS checks should succeed while moving row from part_a_10_a_20 to --- part_d_1_15. Even though the UPDATE is setting 'c' to an odd number, the --- trigger at the destination partition again makes it an even number. -UPDATE range_parted set a = 'b', c = 151 WHERE a = 'a' and c = 200; -RESET SESSION AUTHORIZATION; -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- This should fail with RLS violation error. Even though the UPDATE is setting --- 'c' to an even number, the trigger at the destination partition again makes --- it an odd number. -UPDATE range_parted set a = 'b', c = 150 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy for table "range_parted" --- Cleanup -RESET SESSION AUTHORIZATION; -DROP TRIGGER trig_d_1_15 ON part_d_1_15; -DROP FUNCTION func_d_1_15(); --- Policy expression contains SubPlan -RESET SESSION AUTHORIZATION; -:init_range_parted; -CREATE POLICY policy_range_parted_subplan on range_parted - AS RESTRICTIVE for UPDATE USING (true) - WITH CHECK ((SELECT range_parted.c <= c1 FROM mintab)); -SET SESSION AUTHORIZATION regress_range_parted_user; --- fail, mintab has row with c1 = 120 -UPDATE range_parted set a = 'b', c = 122 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy "policy_range_parted_subplan" for table "range_parted" --- ok -UPDATE range_parted set a = 'b', c = 120 WHERE a = 'a' and c = 200; --- RLS policy expression contains whole row. -RESET SESSION AUTHORIZATION; -:init_range_parted; -CREATE POLICY policy_range_parted_wholerow on range_parted AS RESTRICTIVE for UPDATE USING (true) - WITH CHECK (range_parted = row('b', 10, 112, 1, NULL)::range_parted); -SET SESSION AUTHORIZATION regress_range_parted_user; --- ok, should pass the RLS check -UPDATE range_parted set a = 'b', c = 112 WHERE a = 'a' and c = 200; -RESET SESSION AUTHORIZATION; -:init_range_parted; -SET SESSION AUTHORIZATION regress_range_parted_user; --- fail, the whole row RLS check should fail -UPDATE range_parted set a = 'b', c = 116 WHERE a = 'a' and c = 200; -ERROR: new row violates row-level security policy "policy_range_parted_wholerow" for table "range_parted" --- Cleanup -RESET SESSION AUTHORIZATION; -DROP POLICY policy_range_parted ON range_parted; -DROP POLICY policy_range_parted_subplan ON range_parted; -DROP POLICY policy_range_parted_wholerow ON range_parted; -REVOKE ALL ON range_parted, mintab FROM regress_range_parted_user; -DROP USER regress_range_parted_user; -DROP TABLE mintab; --- statement triggers with update row movement ---------------------------------------------------- -:init_range_parted; -CREATE FUNCTION trigfunc() returns trigger language plpgsql as -$$ - begin - raise notice 'trigger = % fired on table % during %', - TG_NAME, TG_TABLE_NAME, TG_OP; - return null; - end; -$$; --- Triggers on root partition -CREATE TRIGGER parent_delete_trig - AFTER DELETE ON range_parted for each statement execute procedure trigfunc(); -CREATE TRIGGER parent_update_trig - AFTER UPDATE ON range_parted for each statement execute procedure trigfunc(); -CREATE TRIGGER parent_insert_trig - AFTER INSERT ON range_parted for each statement execute procedure trigfunc(); --- Triggers on leaf partition part_c_1_100 -CREATE TRIGGER c1_delete_trig - AFTER DELETE ON part_c_1_100 for each statement execute procedure trigfunc(); -CREATE TRIGGER c1_update_trig - AFTER UPDATE ON part_c_1_100 for each statement execute procedure trigfunc(); -CREATE TRIGGER c1_insert_trig - AFTER INSERT ON part_c_1_100 for each statement execute procedure trigfunc(); --- Triggers on leaf partition part_d_1_15 -CREATE TRIGGER d1_delete_trig - AFTER DELETE ON part_d_1_15 for each statement execute procedure trigfunc(); -CREATE TRIGGER d1_update_trig - AFTER UPDATE ON part_d_1_15 for each statement execute procedure trigfunc(); -CREATE TRIGGER d1_insert_trig - AFTER INSERT ON part_d_1_15 for each statement execute procedure trigfunc(); --- Triggers on leaf partition part_d_15_20 -CREATE TRIGGER d15_delete_trig - AFTER DELETE ON part_d_15_20 for each statement execute procedure trigfunc(); -CREATE TRIGGER d15_update_trig - AFTER UPDATE ON part_d_15_20 for each statement execute procedure trigfunc(); -CREATE TRIGGER d15_insert_trig - AFTER INSERT ON part_d_15_20 for each statement execute procedure trigfunc(); --- Move all rows from part_c_100_200 to part_c_1_100. None of the delete or --- insert statement triggers should be fired. -UPDATE range_parted set c = c - 50 WHERE c > 97; -NOTICE: trigger = parent_update_trig fired on table range_parted during UPDATE -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 150 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_c_1_100 | b | 15 | 55 | 16 | - part_c_1_100 | b | 17 | 55 | 19 | -(6 rows) - -DROP TRIGGER parent_delete_trig ON range_parted; -DROP TRIGGER parent_update_trig ON range_parted; -DROP TRIGGER parent_insert_trig ON range_parted; -DROP TRIGGER c1_delete_trig ON part_c_1_100; -DROP TRIGGER c1_update_trig ON part_c_1_100; -DROP TRIGGER c1_insert_trig ON part_c_1_100; -DROP TRIGGER d1_delete_trig ON part_d_1_15; -DROP TRIGGER d1_update_trig ON part_d_1_15; -DROP TRIGGER d1_insert_trig ON part_d_1_15; -DROP TRIGGER d15_delete_trig ON part_d_15_20; -DROP TRIGGER d15_update_trig ON part_d_15_20; -DROP TRIGGER d15_insert_trig ON part_d_15_20; --- Creating default partition for range -:init_range_parted; -create table part_def partition of range_parted default; -\d+ part_def - Table "public.part_def" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+-------------------+-----------+----------+---------+----------+--------------+------------- - a | text | | | | extended | | - b | bigint | | | | plain | | - c | numeric | | | | main | | - d | integer | | | | plain | | - e | character varying | | | | extended | | -Partition of: range_parted DEFAULT -Partition constraint: (NOT ((a IS NOT NULL) AND (b IS NOT NULL) AND (((a = 'a'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'a'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '1'::bigint) AND (b < '10'::bigint)) OR ((a = 'b'::text) AND (b >= '10'::bigint) AND (b < '20'::bigint)) OR ((a = 'b'::text) AND (b >= '20'::bigint) AND (b < '30'::bigint))))) - -insert into range_parted values ('c', 9); --- ok -update part_def set a = 'd' where a = 'c'; --- fail -update part_def set a = 'a' where a = 'd'; -ERROR: new row for relation "part_def" violates partition constraint -DETAIL: Failing row contains (a, 9, null, null, null). -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_d_15_20 | b | 15 | 105 | 16 | - part_d_15_20 | b | 17 | 105 | 19 | - part_def | d | 9 | | | -(7 rows) - --- Update row movement from non-default to default partition. --- fail, default partition is not under part_a_10_a_20; -UPDATE part_a_10_a_20 set a = 'ad' WHERE a = 'a'; -ERROR: new row for relation "part_a_10_a_20" violates partition constraint -DETAIL: Failing row contains (ad, 10, 200, 1, null). --- ok -UPDATE range_parted set a = 'ad' WHERE a = 'a'; -UPDATE range_parted set a = 'bd' WHERE a = 'b'; -:show_data; - partname | a | b | c | d | e -----------+----+----+-----+----+--- - part_def | ad | 1 | 1 | 1 | - part_def | ad | 10 | 200 | 1 | - part_def | bd | 12 | 96 | 1 | - part_def | bd | 13 | 97 | 2 | - part_def | bd | 15 | 105 | 16 | - part_def | bd | 17 | 105 | 19 | - part_def | d | 9 | | | -(7 rows) - --- Update row movement from default to non-default partitions. --- ok -UPDATE range_parted set a = 'a' WHERE a = 'ad'; -UPDATE range_parted set a = 'b' WHERE a = 'bd'; -:show_data; - partname | a | b | c | d | e -----------------+---+----+-----+----+--- - part_a_10_a_20 | a | 10 | 200 | 1 | - part_a_1_a_10 | a | 1 | 1 | 1 | - part_c_1_100 | b | 12 | 96 | 1 | - part_c_1_100 | b | 13 | 97 | 2 | - part_d_15_20 | b | 15 | 105 | 16 | - part_d_15_20 | b | 17 | 105 | 19 | - part_def | d | 9 | | | -(7 rows) - --- Cleanup: range_parted no longer needed. -DROP TABLE range_parted; -CREATE TABLE list_parted ( - a text, - b int -) PARTITION BY list (a); -CREATE TABLE list_part1 PARTITION OF list_parted for VALUES in ('a', 'b'); -CREATE TABLE list_default PARTITION OF list_parted default; -INSERT into list_part1 VALUES ('a', 1); -INSERT into list_default VALUES ('d', 10); --- fail -UPDATE list_default set a = 'a' WHERE a = 'd'; -ERROR: new row for relation "list_default" violates partition constraint -DETAIL: Failing row contains (a, 10). --- ok -UPDATE list_default set a = 'x' WHERE a = 'd'; -DROP TABLE list_parted; --- Test retrieval of system columns with non-consistent partition row types. --- This is only partially supported, as seen in the results. -create table utrtest (a int, b text) partition by list (a); -create table utr1 (a int check (a in (1)), q text, b text); -create table utr2 (a int check (a in (2)), b text); -alter table utr1 drop column q; -alter table utrtest attach partition utr1 for values in (1); -alter table utrtest attach partition utr2 for values in (2); -insert into utrtest values (1, 'foo') - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; - a | b | tableoid | xmin_ok ----+-----+----------+--------- - 1 | foo | utr1 | t -(1 row) - -insert into utrtest values (2, 'bar') - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: cannot retrieve a system column in this context -insert into utrtest values (2, 'bar') - returning *, tableoid::regclass; - a | b | tableoid ----+-----+---------- - 2 | bar | utr2 -(1 row) - -update utrtest set b = b || b from (values (1), (2)) s(x) where a = s.x - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; - a | b | x | tableoid | xmin_ok ----+--------+---+----------+--------- - 1 | foofoo | 1 | utr1 | t - 2 | barbar | 2 | utr2 | t -(2 rows) - -update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x - returning *, tableoid::regclass, xmin = pg_current_xact_id()::xid as xmin_ok; -- fails -ERROR: cannot retrieve a system column in this context -update utrtest set a = 3 - a from (values (1), (2)) s(x) where a = s.x - returning *, tableoid::regclass; - a | b | x | tableoid ----+--------+---+---------- - 2 | foofoo | 1 | utr2 - 1 | barbar | 2 | utr1 -(2 rows) - -delete from utrtest - returning *, tableoid::regclass, xmax = pg_current_xact_id()::xid as xmax_ok; - a | b | tableoid | xmax_ok ----+--------+----------+--------- - 1 | barbar | utr1 | t - 2 | foofoo | utr2 | t -(2 rows) - -drop table utrtest; --------------- --- Some more update-partition-key test scenarios below. This time use list --- partitions. --------------- --- Setup for list partitions -CREATE TABLE list_parted (a numeric, b int, c int8) PARTITION BY list (a); -CREATE TABLE sub_parted PARTITION OF list_parted for VALUES in (1) PARTITION BY list (b); -CREATE TABLE sub_part1(b int, c int8, a numeric); -ALTER TABLE sub_parted ATTACH PARTITION sub_part1 for VALUES in (1); -CREATE TABLE sub_part2(b int, c int8, a numeric); -ALTER TABLE sub_parted ATTACH PARTITION sub_part2 for VALUES in (2); -CREATE TABLE list_part1(a numeric, b int, c int8); -ALTER TABLE list_parted ATTACH PARTITION list_part1 for VALUES in (2,3); -INSERT into list_parted VALUES (2,5,50); -INSERT into list_parted VALUES (3,6,60); -INSERT into sub_parted VALUES (1,1,60); -INSERT into sub_parted VALUES (1,2,10); --- Test partition constraint violation when intermediate ancestor is used and --- constraint is inherited from upper root. -UPDATE sub_parted set a = 2 WHERE c = 10; -ERROR: new row for relation "sub_parted" violates partition constraint -DETAIL: Failing row contains (2, 2, 10). --- Test update-partition-key, where the unpruned partitions do not have their --- partition keys updated. -SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; - tableoid | a | b | c -------------+---+---+---- - list_part1 | 2 | 5 | 50 -(1 row) - -UPDATE list_parted set b = c + a WHERE a = 2; -SELECT tableoid::regclass::text, * FROM list_parted WHERE a = 2 ORDER BY 1; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 -(1 row) - --- Test the case where BR UPDATE triggers change the partition key. -CREATE FUNCTION func_parted_mod_b() returns trigger as $$ -BEGIN - NEW.b = 2; -- This is changing partition key column. - return NEW; -END $$ LANGUAGE plpgsql; -CREATE TRIGGER parted_mod_b before update on sub_part1 - for each row execute procedure func_parted_mod_b(); -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part1 | 1 | 1 | 60 - sub_part2 | 1 | 2 | 10 -(4 rows) - --- This should do the tuple routing even though there is no explicit --- partition-key update, because there is a trigger on sub_part1. -UPDATE list_parted set c = 70 WHERE b = 1; -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part2 | 1 | 2 | 10 - sub_part2 | 1 | 2 | 70 -(4 rows) - -DROP TRIGGER parted_mod_b ON sub_part1; --- If BR DELETE trigger prevented DELETE from happening, we should also skip --- the INSERT if that delete is part of UPDATE=>DELETE+INSERT. -CREATE OR REPLACE FUNCTION func_parted_mod_b() returns trigger as $$ -BEGIN - raise notice 'Trigger: Got OLD row %, but returning NULL', OLD; - return NULL; -END $$ LANGUAGE plpgsql; -CREATE TRIGGER trig_skip_delete before delete on sub_part2 - for each row execute procedure func_parted_mod_b(); -UPDATE list_parted set b = 1 WHERE c = 70; -NOTICE: Trigger: Got OLD row (2,70,1), but returning NULL -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part2 | 1 | 2 | 10 - sub_part2 | 1 | 2 | 70 -(4 rows) - --- Drop the trigger. Now the row should be moved. -DROP TRIGGER trig_skip_delete ON sub_part2; -UPDATE list_parted set b = 1 WHERE c = 70; -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 - sub_part1 | 1 | 1 | 70 - sub_part2 | 1 | 2 | 10 -(4 rows) - -DROP FUNCTION func_parted_mod_b(); --- UPDATE partition-key with FROM clause. If join produces multiple output --- rows for the same row to be modified, we should tuple-route the row only --- once. There should not be any rows inserted. -CREATE TABLE non_parted (id int); -INSERT into non_parted VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); -UPDATE list_parted t1 set a = 2 FROM non_parted t2 WHERE t1.a = t2.id and a = 1; -SELECT tableoid::regclass::text, * FROM list_parted ORDER BY 1, 2, 3, 4; - tableoid | a | b | c -------------+---+----+---- - list_part1 | 2 | 1 | 70 - list_part1 | 2 | 2 | 10 - list_part1 | 2 | 52 | 50 - list_part1 | 3 | 6 | 60 -(4 rows) - -DROP TABLE non_parted; --- Cleanup: list_parted no longer needed. -DROP TABLE list_parted; --- create custom operator class and hash function, for the same reason --- explained in alter_table.sql -create or replace function dummy_hashint4(a int4, seed int8) returns int8 as -$$ begin return (a + seed); end; $$ language 'plpgsql' immutable; -create operator class custom_opclass for type int4 using hash as -operator 1 = , function 2 dummy_hashint4(int4, int8); -create table hash_parted ( - a int, - b int -) partition by hash (a custom_opclass, b custom_opclass); -create table hpart1 partition of hash_parted for values with (modulus 2, remainder 1); -create table hpart2 partition of hash_parted for values with (modulus 4, remainder 2); -create table hpart3 partition of hash_parted for values with (modulus 8, remainder 0); -create table hpart4 partition of hash_parted for values with (modulus 8, remainder 4); -insert into hpart1 values (1, 1); -insert into hpart2 values (2, 5); -insert into hpart4 values (3, 4); --- fail -update hpart1 set a = 3, b=4 where a = 1; -ERROR: new row for relation "hpart1" violates partition constraint -DETAIL: Failing row contains (3, 4). --- ok, row movement -update hash_parted set b = b - 1 where b = 1; --- ok -update hash_parted set b = b + 8 where b = 1; --- cleanup -drop table hash_parted; -drop operator class custom_opclass using hash; -drop function dummy_hashint4(a int4, seed int8); +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/groupingsets.out /tmp/cirrus-ci-build/src/test/regress/results/groupingsets.out --- /tmp/cirrus-ci-build/src/test/regress/expected/groupingsets.out 2026-03-09 20:33:45.156311188 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/groupingsets.out 2026-03-09 20:39:17.120088028 +0000 @@ -305,2461 +305,8 @@ -- empty input with joins tests some important code paths select t1.a, t2.b, sum(t1.v), count(*) from gstest_empty t1, gstest_empty t2 group by grouping sets ((t1.a,t2.b),()); - a | b | sum | count ----+---+-----+------- - | | | 0 -(1 row) - --- simple joins, var resolution, GROUPING on join vars -select t1.a, t2.b, grouping(t1.a, t2.b), sum(t1.v), max(t2.a) - from gstest1 t1, gstest2 t2 - group by grouping sets ((t1.a, t2.b), ()); - a | b | grouping | sum | max ----+---+----------+------+----- - 1 | 1 | 0 | 420 | 1 - 1 | 2 | 0 | 120 | 2 - 2 | 1 | 0 | 105 | 1 - 2 | 2 | 0 | 30 | 2 - 3 | 1 | 0 | 231 | 1 - 3 | 2 | 0 | 66 | 2 - 4 | 1 | 0 | 259 | 1 - 4 | 2 | 0 | 74 | 2 - | | 3 | 1305 | 2 -(9 rows) - -select t1.a, t2.b, grouping(t1.a, t2.b), sum(t1.v), max(t2.a) - from gstest1 t1 join gstest2 t2 on (t1.a=t2.a) - group by grouping sets ((t1.a, t2.b), ()); - a | b | grouping | sum | max ----+---+----------+-----+----- - 1 | 1 | 0 | 420 | 1 - 1 | 2 | 0 | 60 | 1 - 2 | 2 | 0 | 15 | 2 - | | 3 | 495 | 2 -(4 rows) - -select a, b, grouping(a, b), sum(t1.v), max(t2.c) - from gstest1 t1 join gstest2 t2 using (a,b) - group by grouping sets ((a, b), ()); - a | b | grouping | sum | max ----+---+----------+-----+----- - 1 | 1 | 0 | 147 | 2 - 1 | 2 | 0 | 25 | 2 - | | 3 | 172 | 2 -(3 rows) - -select a, b, grouping(a, b), sum(t1.v), max(t2.c) - from gstest1 t1 full join gstest2 t2 using (a,b) - group by grouping sets ((a, b), ()); - a | b | grouping | sum | max ----+---+----------+-----+----- - 1 | 1 | 0 | 147 | 2 - 1 | 2 | 0 | 25 | 2 - 1 | 3 | 0 | 14 | - 2 | 2 | 0 | | 2 - 2 | 3 | 0 | 15 | - 3 | 3 | 0 | 16 | - 3 | 4 | 0 | 17 | - 4 | 1 | 0 | 37 | - | | 3 | 271 | 2 -(9 rows) - --- references in subqueries should work too -select (select a), - (select b), - (select grouping(a, b)), - (select sum(t1.v)), - (select max(t2.c)) - from gstest1 t1 full join gstest2 t2 using (a,b) - group by grouping sets ((a, b), ()); - a | b | grouping | sum | max ----+---+----------+-----+----- - 1 | 1 | 0 | 147 | 2 - 1 | 2 | 0 | 25 | 2 - 1 | 3 | 0 | 14 | - 2 | 2 | 0 | | 2 - 2 | 3 | 0 | 15 | - 3 | 3 | 0 | 16 | - 3 | 4 | 0 | 17 | - 4 | 1 | 0 | 37 | - | | 3 | 271 | 2 -(9 rows) - --- check that functionally dependent cols are not nulled -select a, d, grouping(a,b,c) - from gstest3 - group by grouping sets ((a,b), (a,c)); - a | d | grouping ----+---+---------- - 1 | 1 | 1 - 2 | 2 | 1 - 1 | 1 | 2 - 2 | 2 | 2 -(4 rows) - --- check that distinct grouping columns are kept separate --- even if they are equal() -explain (costs off) -select g as alias1, g as alias2 - from generate_series(1,3) g - group by alias1, rollup(alias2); - QUERY PLAN ------------------------------------------------- - GroupAggregate - Group Key: g, g - Group Key: g - -> Sort - Sort Key: g - -> Function Scan on generate_series g -(6 rows) - -select g as alias1, g as alias2 - from generate_series(1,3) g - group by alias1, rollup(alias2); - alias1 | alias2 ---------+-------- - 1 | 1 - 1 | - 2 | 2 - 2 | - 3 | 3 - 3 | -(6 rows) - --- check that pulled-up subquery outputs still go to null when appropriate -select four, x - from (select four, ten, 'foo'::text as x from tenk1) as t - group by grouping sets (four, x) - having x = 'foo'; - four | x -------+----- - | foo -(1 row) - -select four, x || 'x' - from (select four, ten, 'foo'::text as x from tenk1) as t - group by grouping sets (four, x) - order by four; - four | ?column? -------+---------- - 0 | - 1 | - 2 | - 3 | - | foox -(5 rows) - -select (x+y)*1, sum(z) - from (select 1 as x, 2 as y, 3 as z) s - group by grouping sets (x+y, x); - ?column? | sum -----------+----- - 3 | 3 - | 3 -(2 rows) - -select x, not x as not_x, q2 from - (select *, q1 = 1 as x from int8_tbl i1) as t - group by grouping sets(x, q2) - order by x, q2; - x | not_x | q2 ----+-------+------------------- - f | t | - | | -4567890123456789 - | | 123 - | | 456 - | | 4567890123456789 -(5 rows) - -select x, y - from (select four as x, four as y from tenk1) as t - group by grouping sets (x, y) - having y is null - order by 1, 2; - x | y ----+--- - 0 | - 1 | - 2 | - 3 | -(4 rows) - -select x, y || 'y' - from (select four as x, four as y from tenk1) as t - group by grouping sets (x, y) - order by 1, 2; - x | ?column? ----+---------- - 0 | - 1 | - 2 | - 3 | - | 0y - | 1y - | 2y - | 3y -(8 rows) - --- check that operands wrapped in PlaceHolderVars are capable of index matching -begin; -set local enable_bitmapscan = off; -explain (costs off) -select x, y - from (select unique1 as x, unique2 as y from tenk1) as t - where x = 1 - group by grouping sets (x, y) - order by 1, 2; - QUERY PLAN ------------------------------------------------------ - Sort - Sort Key: tenk1.unique1, tenk1.unique2 - -> GroupAggregate - Group Key: tenk1.unique1 - Sort Key: tenk1.unique2 - Group Key: tenk1.unique2 - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 = 1) -(8 rows) - -select x, y - from (select unique1 as x, unique2 as y from tenk1) as t - where x = 1 - group by grouping sets (x, y) - order by 1, 2; - x | y ----+------ - 1 | - | 2838 -(2 rows) - -explain (costs off) -select x, y - from (select unique1::oid as x, unique2 as y from tenk1) as t - where x::integer = 1 - group by grouping sets (x, y) - order by 1, 2; - QUERY PLAN ------------------------------------------------------------------ - Sort - Sort Key: ((tenk1.unique1)::oid), tenk1.unique2 - -> GroupAggregate - Group Key: ((tenk1.unique1)::oid) - Sort Key: tenk1.unique2 - Group Key: tenk1.unique2 - -> Sort - Sort Key: ((tenk1.unique1)::oid) - -> Index Scan using tenk1_unique1 on tenk1 - Index Cond: (((unique1)::oid)::integer = 1) -(10 rows) - -select x, y - from (select unique1::oid as x, unique2 as y from tenk1) as t - where x::integer = 1 - group by grouping sets (x, y) - order by 1, 2; - x | y ----+------ - 1 | - | 2838 -(2 rows) - -explain (costs off) -select x, y - from (select t1.unique1 as x, t1.unique2 as y from tenk1 t1, tenk1 t2) as t - where x = 1 - group by grouping sets (x, y) - order by 1, 2; - QUERY PLAN -------------------------------------------------------------------- - Sort - Sort Key: t1.unique1, t1.unique2 - -> GroupAggregate - Group Key: t1.unique1 - Sort Key: t1.unique2 - Group Key: t1.unique2 - -> Nested Loop - -> Index Scan using tenk1_unique1 on tenk1 t1 - Index Cond: (unique1 = 1) - -> Index Only Scan using tenk1_hundred on tenk1 t2 -(10 rows) - -select x, y - from (select t1.unique1 as x, t1.unique2 as y from tenk1 t1, tenk1 t2) as t - where x = 1 - group by grouping sets (x, y) - order by 1, 2; - x | y ----+------ - 1 | - | 2838 -(2 rows) - -rollback; --- check qual push-down rules for a subquery with grouping sets -explain (verbose, costs off) -select * from ( - select 1 as x, q1, sum(q2) - from int8_tbl i1 - group by grouping sets(1, 2) -) ss -where x = 1 and q1 = 123; - QUERY PLAN --------------------------------------------------- - Subquery Scan on ss - Output: ss.x, ss.q1, ss.sum - Filter: ((ss.x = 1) AND (ss.q1 = 123)) - -> GroupAggregate - Output: (1), i1.q1, sum(i1.q2) - Group Key: (1) - Sort Key: i1.q1 - Group Key: i1.q1 - -> Sort - Output: (1), i1.q1, i1.q2 - Sort Key: (1) - -> Seq Scan on public.int8_tbl i1 - Output: 1, i1.q1, i1.q2 -(13 rows) - -select * from ( - select 1 as x, q1, sum(q2) - from int8_tbl i1 - group by grouping sets(1, 2) -) ss -where x = 1 and q1 = 123; - x | q1 | sum ----+----+----- -(0 rows) - --- check handling of pulled-up SubPlan in GROUPING() argument (bug #17479) -explain (verbose, costs off) -select grouping(ss.x) -from int8_tbl i1 -cross join lateral (select (select i1.q1) as x) ss -group by ss.x; - QUERY PLAN ----------------------------------------------------------- - GroupAggregate - Output: GROUPING((SubPlan expr_1)), ((SubPlan expr_2)) - Group Key: ((SubPlan expr_2)) - -> Sort - Output: ((SubPlan expr_2)), i1.q1 - Sort Key: ((SubPlan expr_2)) - -> Seq Scan on public.int8_tbl i1 - Output: (SubPlan expr_2), i1.q1 - SubPlan expr_2 - -> Result - Output: i1.q1 -(11 rows) - -select grouping(ss.x) -from int8_tbl i1 -cross join lateral (select (select i1.q1) as x) ss -group by ss.x; - grouping ----------- - 0 - 0 -(2 rows) - -explain (verbose, costs off) -select (select grouping(ss.x)) -from int8_tbl i1 -cross join lateral (select (select i1.q1) as x) ss -group by ss.x; - QUERY PLAN ------------------------------------------------- - GroupAggregate - Output: (SubPlan expr_1), ((SubPlan expr_3)) - Group Key: ((SubPlan expr_3)) - -> Sort - Output: ((SubPlan expr_3)), i1.q1 - Sort Key: ((SubPlan expr_3)) - -> Seq Scan on public.int8_tbl i1 - Output: (SubPlan expr_3), i1.q1 - SubPlan expr_3 - -> Result - Output: i1.q1 - SubPlan expr_1 - -> Result - Output: GROUPING((SubPlan expr_2)) -(14 rows) - -select (select grouping(ss.x)) -from int8_tbl i1 -cross join lateral (select (select i1.q1) as x) ss -group by ss.x; - grouping ----------- - 0 - 0 -(2 rows) - --- simple rescan tests -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by rollup (a,b); - a | b | sum ----+---+----- - 1 | 1 | 1 - 1 | 2 | 1 - 1 | 3 | 1 - 1 | | 3 - 2 | 1 | 2 - 2 | 2 | 2 - 2 | 3 | 2 - 2 | | 6 - | | 9 -(9 rows) - -select * - from (values (1),(2)) v(x), - lateral (select a, b, sum(v.x) from gstest_data(v.x) group by rollup (a,b)) s; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 3: lateral (select a, b, sum(v.x) from gstest_data(v.x) ... - ^ --- min max optimization should still work with GROUP BY () -explain (costs off) - select min(unique1) from tenk1 GROUP BY (); - QUERY PLAN ------------------------------------------------------------- - Result - Replaces: MinMaxAggregate - InitPlan minmax_1 - -> Limit - -> Index Only Scan using tenk1_unique1 on tenk1 - Index Cond: (unique1 IS NOT NULL) -(6 rows) - --- Views with GROUPING SET queries -CREATE VIEW gstest_view AS select a, b, grouping(a,b), sum(c), count(*), max(c) - from gstest2 group by rollup ((a,b,c),(c,d)); -NOTICE: view "gstest_view" will be a temporary view -DETAIL: It depends on temporary table gstest2. -select pg_get_viewdef('gstest_view'::regclass, true); - pg_get_viewdef ---------------------------------------- - SELECT a, + - b, + - GROUPING(a, b) AS "grouping", + - sum(c) AS sum, + - count(*) AS count, + - max(c) AS max + - FROM gstest2 + - GROUP BY ROLLUP((a, b, c), (c, d)); -(1 row) - --- Nested queries with 3 or more levels of nesting -select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); - grouping ----------- - 0 - 0 - 0 -(3 rows) - -select(select (select grouping(e,f) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); - grouping ----------- - 0 - 1 - 3 -(3 rows) - -select(select (select grouping(c) from (values (1)) v2(c) GROUP BY c) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP(e,f); - grouping ----------- - 0 - 0 - 0 -(3 rows) - --- Combinations of operations -select a, b, c, d from gstest2 group by rollup(a,b),grouping sets(c,d); - a | b | c | d ----+---+---+--- - 1 | 1 | 1 | - 1 | | 1 | - | | 1 | - 1 | 1 | 2 | - 1 | 2 | 2 | - 1 | | 2 | - 2 | 2 | 2 | - 2 | | 2 | - | | 2 | - 1 | 1 | | 1 - 1 | | | 1 - | | | 1 - 1 | 1 | | 2 - 1 | 2 | | 2 - 1 | | | 2 - 2 | 2 | | 2 - 2 | | | 2 - | | | 2 -(18 rows) - -select a, b from (values (1,2),(2,3)) v(a,b) group by a,b, grouping sets(a); - a | b ----+--- - 1 | 2 - 2 | 3 -(2 rows) - --- Tests for chained aggregates -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - | | 3 | 21 | 2 | 11 - | | 3 | 21 | 2 | 11 - | | 3 | 25 | 2 | 13 - | | 3 | 25 | 2 | 13 - | | 3 | 14 | 1 | 14 - | | 3 | 14 | 1 | 14 - | | 3 | 15 | 1 | 15 - | | 3 | 15 | 1 | 15 - | | 3 | 16 | 1 | 16 - | | 3 | 16 | 1 | 16 - | | 3 | 17 | 1 | 17 - | | 3 | 17 | 1 | 17 - | | 3 | 37 | 2 | 19 - | | 3 | 37 | 2 | 19 -(21 rows) - -select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY ROLLUP((e+1),(f+1)); - grouping ----------- - 0 - 0 - 0 -(3 rows) - -select(select (select grouping(a,b) from (values (1)) v2(c)) from (values (1,2)) v1(a,b) group by (a,b)) from (values(6,7)) v3(e,f) GROUP BY CUBE((e+1),(f+1)) ORDER BY (e+1),(f+1); - grouping ----------- - 0 - 0 - 0 - 0 -(4 rows) - -select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by cube (a,b) order by rsum, a, b; - a | b | sum | rsum ----+---+-----+------ - 1 | 1 | 8 | 8 - 1 | 2 | 2 | 10 - 1 | | 10 | 20 - 2 | 2 | 2 | 22 - 2 | | 2 | 24 - | 1 | 8 | 32 - | 2 | 4 | 36 - | | 12 | 48 -(8 rows) - -select a, b, sum(c) from (values (1,1,10),(1,1,11),(1,2,12),(1,2,13),(1,3,14),(2,3,15),(3,3,16),(3,4,17),(4,1,18),(4,1,19)) v(a,b,c) group by rollup (a,b); - a | b | sum ----+---+----- - 1 | 1 | 21 - 1 | 2 | 25 - 1 | 3 | 14 - 1 | | 60 - 2 | 3 | 15 - 2 | | 15 - 3 | 3 | 16 - 3 | 4 | 17 - 3 | | 33 - 4 | 1 | 37 - 4 | | 37 - | | 145 -(12 rows) - -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by cube (a,b) order by a,b; - a | b | sum ----+---+----- - 1 | 1 | 1 - 1 | 2 | 1 - 1 | 3 | 1 - 1 | | 3 - 2 | 1 | 2 - 2 | 2 | 2 - 2 | 3 | 2 - 2 | | 6 - | 1 | 3 - | 2 | 3 - | 3 | 3 - | | 9 -(12 rows) - --- Test reordering of grouping sets -explain (costs off) -select * from gstest1 group by grouping sets((a,b,v),(v)) order by v,b,a; - QUERY PLAN ------------------------------------------------------------------------------------- - Incremental Sort - Sort Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1 - Presorted Key: "*VALUES*".column3 - -> GroupAggregate - Group Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1 - Group Key: "*VALUES*".column3 - -> Sort - Sort Key: "*VALUES*".column3, "*VALUES*".column2, "*VALUES*".column1 - -> Values Scan on "*VALUES*" -(9 rows) - --- Agg level check. This query should error out. -select (select grouping(a,b) from gstest2) from gstest2 group by a,b; -ERROR: arguments to GROUPING must be grouping expressions of the associated query level -LINE 1: select (select grouping(a,b) from gstest2) from gstest2 grou... - ^ ---Nested queries -select a, b, sum(c), count(*) from gstest2 group by grouping sets (rollup(a,b),a); - a | b | sum | count ----+---+-----+------- - 1 | 1 | 8 | 7 - 1 | 2 | 2 | 1 - 1 | | 10 | 8 - 1 | | 10 | 8 - 2 | 2 | 2 | 1 - 2 | | 2 | 1 - 2 | | 2 | 1 - | | 12 | 9 -(8 rows) - --- HAVING queries -select ten, sum(distinct four) from onek a -group by grouping sets((ten,four),(ten)) -having exists (select 1 from onek b where sum(distinct a.four) = b.four); - ten | sum ------+----- - 0 | 0 - 0 | 2 - 0 | 2 - 1 | 1 - 1 | 3 - 2 | 0 - 2 | 2 - 2 | 2 - 3 | 1 - 3 | 3 - 4 | 0 - 4 | 2 - 4 | 2 - 5 | 1 - 5 | 3 - 6 | 0 - 6 | 2 - 6 | 2 - 7 | 1 - 7 | 3 - 8 | 0 - 8 | 2 - 8 | 2 - 9 | 1 - 9 | 3 -(25 rows) - --- Tests around pushdown of HAVING clauses, partially testing against previous bugs -select a,count(*) from gstest2 group by rollup(a) order by a; - a | count ----+------- - 1 | 8 - 2 | 1 - | 9 -(3 rows) - -select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a; - a | count ----+------- - 2 | 1 - | 9 -(2 rows) - -explain (costs off) - select a,count(*) from gstest2 group by rollup(a) having a is distinct from 1 order by a; - QUERY PLAN ----------------------------------------- - Sort - Sort Key: a - -> GroupAggregate - Group Key: a - Group Key: () - Filter: (a IS DISTINCT FROM 1) - -> Sort - Sort Key: a - -> Seq Scan on gstest2 -(9 rows) - -select v.c, (select count(*) from gstest2 group by () having v.c) - from (values (false),(true)) v(c) order by v.c; - c | count ----+------- - f | - t | 9 -(2 rows) - -explain (costs off) - select v.c, (select count(*) from gstest2 group by () having v.c) - from (values (false),(true)) v(c) order by v.c; - QUERY PLAN ------------------------------------------------------------ - Sort - Sort Key: "*VALUES*".column1 - -> Values Scan on "*VALUES*" - SubPlan expr_1 - -> Aggregate - Group Key: () - Filter: "*VALUES*".column1 - -> Result - One-Time Filter: "*VALUES*".column1 - -> Seq Scan on gstest2 -(10 rows) - --- test pushdown of non-degenerate HAVING clause that does not reference any --- columns that are nullable by grouping sets -explain (costs off) -select a, b, count(*) from gstest2 group by grouping sets ((a, b), (a)) having a > 1 and b > 1; - QUERY PLAN ---------------------------------- - GroupAggregate - Group Key: a, b - Group Key: a - Filter: (b > 1) - -> Sort - Sort Key: a, b - -> Seq Scan on gstest2 - Filter: (a > 1) -(8 rows) - -select a, b, count(*) from gstest2 group by grouping sets ((a, b), (a)) having a > 1 and b > 1; - a | b | count ----+---+------- - 2 | 2 | 1 -(1 row) - -explain (costs off) -select a, b, count(*) from gstest2 group by rollup(a), b having b > 1; - QUERY PLAN ---------------------------------- - GroupAggregate - Group Key: b, a - Group Key: b - -> Sort - Sort Key: b, a - -> Seq Scan on gstest2 - Filter: (b > 1) -(7 rows) - -select a, b, count(*) from gstest2 group by rollup(a), b having b > 1; - a | b | count ----+---+------- - 1 | 2 | 1 - 2 | 2 | 1 - | 2 | 2 -(3 rows) - --- test pushdown of degenerate HAVING clause -explain (costs off) -select count(*) from gstest2 group by grouping sets (()) having false; - QUERY PLAN ------------------------------------ - Aggregate - Group Key: () - Filter: false - -> Result - Replaces: Scan on gstest2 - One-Time Filter: false -(6 rows) - -select count(*) from gstest2 group by grouping sets (()) having false; - count -------- -(0 rows) - -explain (costs off) -select a, count(*) from gstest2 group by grouping sets ((a), ()) having false; - QUERY PLAN ------------------------------------------ - GroupAggregate - Group Key: a - Group Key: () - Filter: false - -> Sort - Sort Key: a - -> Result - Replaces: Scan on gstest2 - One-Time Filter: false -(9 rows) - -select a, count(*) from gstest2 group by grouping sets ((a), ()) having false; - a | count ----+------- -(0 rows) - -explain (costs off) -select a, b, count(*) from gstest2 group by grouping sets ((a), (b)) having false; - QUERY PLAN ------------------------------------------ - GroupAggregate - Group Key: a - Sort Key: b - Group Key: b - -> Sort - Sort Key: a - -> Result - Replaces: Scan on gstest2 - One-Time Filter: false -(9 rows) - -select a, b, count(*) from gstest2 group by grouping sets ((a), (b)) having false; - a | b | count ----+---+------- -(0 rows) - --- HAVING with GROUPING queries -select ten, grouping(ten) from onek -group by grouping sets(ten) having grouping(ten) >= 0 -order by 2,1; - ten | grouping ------+---------- - 0 | 0 - 1 | 0 - 2 | 0 - 3 | 0 - 4 | 0 - 5 | 0 - 6 | 0 - 7 | 0 - 8 | 0 - 9 | 0 -(10 rows) - -select ten, grouping(ten) from onek -group by grouping sets(ten, four) having grouping(ten) > 0 -order by 2,1; - ten | grouping ------+---------- - | 1 - | 1 - | 1 - | 1 -(4 rows) - -select ten, grouping(ten) from onek -group by rollup(ten) having grouping(ten) > 0 -order by 2,1; - ten | grouping ------+---------- - | 1 -(1 row) - -select ten, grouping(ten) from onek -group by cube(ten) having grouping(ten) > 0 -order by 2,1; - ten | grouping ------+---------- - | 1 -(1 row) - -select ten, grouping(ten) from onek -group by (ten) having grouping(ten) >= 0 -order by 2,1; - ten | grouping ------+---------- - 0 | 0 - 1 | 0 - 2 | 0 - 3 | 0 - 4 | 0 - 5 | 0 - 6 | 0 - 7 | 0 - 8 | 0 - 9 | 0 -(10 rows) - --- FILTER queries -select ten, sum(distinct four) filter (where four::text ~ '123') from onek a -group by rollup(ten); - ten | sum ------+----- - 0 | - 1 | - 2 | - 3 | - 4 | - 5 | - 6 | - 7 | - 8 | - 9 | - | -(11 rows) - --- More rescan tests -select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten; - a | a | four | ten | count ----+---+------+-----+------- - 1 | 1 | 0 | 0 | 50 - 1 | 1 | 0 | 2 | 50 - 1 | 1 | 0 | 4 | 50 - 1 | 1 | 0 | 6 | 50 - 1 | 1 | 0 | 8 | 50 - 1 | 1 | 0 | | 250 - 1 | 1 | 1 | 1 | 50 - 1 | 1 | 1 | 3 | 50 - 1 | 1 | 1 | 5 | 50 - 1 | 1 | 1 | 7 | 50 - 1 | 1 | 1 | 9 | 50 - 1 | 1 | 1 | | 250 - 1 | 1 | 2 | 0 | 50 - 1 | 1 | 2 | 2 | 50 - 1 | 1 | 2 | 4 | 50 - 1 | 1 | 2 | 6 | 50 - 1 | 1 | 2 | 8 | 50 - 1 | 1 | 2 | | 250 - 1 | 1 | 3 | 1 | 50 - 1 | 1 | 3 | 3 | 50 - 1 | 1 | 3 | 5 | 50 - 1 | 1 | 3 | 7 | 50 - 1 | 1 | 3 | 9 | 50 - 1 | 1 | 3 | | 250 - 1 | 1 | | 0 | 100 - 1 | 1 | | 1 | 100 - 1 | 1 | | 2 | 100 - 1 | 1 | | 3 | 100 - 1 | 1 | | 4 | 100 - 1 | 1 | | 5 | 100 - 1 | 1 | | 6 | 100 - 1 | 1 | | 7 | 100 - 1 | 1 | | 8 | 100 - 1 | 1 | | 9 | 100 - 1 | 1 | | | 1000 - 2 | 2 | 0 | 0 | 50 - 2 | 2 | 0 | 2 | 50 - 2 | 2 | 0 | 4 | 50 - 2 | 2 | 0 | 6 | 50 - 2 | 2 | 0 | 8 | 50 - 2 | 2 | 0 | | 250 - 2 | 2 | 1 | 1 | 50 - 2 | 2 | 1 | 3 | 50 - 2 | 2 | 1 | 5 | 50 - 2 | 2 | 1 | 7 | 50 - 2 | 2 | 1 | 9 | 50 - 2 | 2 | 1 | | 250 - 2 | 2 | 2 | 0 | 50 - 2 | 2 | 2 | 2 | 50 - 2 | 2 | 2 | 4 | 50 - 2 | 2 | 2 | 6 | 50 - 2 | 2 | 2 | 8 | 50 - 2 | 2 | 2 | | 250 - 2 | 2 | 3 | 1 | 50 - 2 | 2 | 3 | 3 | 50 - 2 | 2 | 3 | 5 | 50 - 2 | 2 | 3 | 7 | 50 - 2 | 2 | 3 | 9 | 50 - 2 | 2 | 3 | | 250 - 2 | 2 | | 0 | 100 - 2 | 2 | | 1 | 100 - 2 | 2 | | 2 | 100 - 2 | 2 | | 3 | 100 - 2 | 2 | | 4 | 100 - 2 | 2 | | 5 | 100 - 2 | 2 | | 6 | 100 - 2 | 2 | | 7 | 100 - 2 | 2 | | 8 | 100 - 2 | 2 | | 9 | 100 - 2 | 2 | | | 1000 -(70 rows) - -select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a); - array ------------------------------------------------------------------------------------------------------------------------------------------------------- - {"(1,0,0,250)","(1,0,2,250)","(1,0,,500)","(1,1,1,250)","(1,1,3,250)","(1,1,,500)","(1,,0,250)","(1,,1,250)","(1,,2,250)","(1,,3,250)","(1,,,1000)"} - {"(2,0,0,250)","(2,0,2,250)","(2,0,,500)","(2,1,1,250)","(2,1,3,250)","(2,1,,500)","(2,,0,250)","(2,,1,250)","(2,,2,250)","(2,,3,250)","(2,,,1000)"} -(2 rows) - --- Grouping on text columns -select sum(ten) from onek group by two, rollup(four::text) order by 1; - sum ------- - 1000 - 1000 - 1250 - 1250 - 2000 - 2500 -(6 rows) - -select sum(ten) from onek group by rollup(four::text), two order by 1; - sum ------- - 1000 - 1000 - 1250 - 1250 - 2000 - 2500 -(6 rows) - --- hashing support -set enable_hashagg = true; --- failure cases -select count(*) from gstest4 group by rollup(unhashable_col,unsortable_col); -ERROR: could not implement GROUP BY -DETAIL: Some of the datatypes only support hashing, while others only support sorting. -select array_agg(v order by v) from gstest4 group by grouping sets ((id,unsortable_col),(id)); -ERROR: could not implement GROUP BY -DETAIL: Some of the datatypes only support hashing, while others only support sorting. --- simple cases -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a),(b)) order by 3,1,2; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | | 1 | 60 | 5 | 14 - 2 | | 1 | 15 | 1 | 15 - 3 | | 1 | 33 | 2 | 17 - 4 | | 1 | 37 | 2 | 19 - | 1 | 2 | 58 | 4 | 19 - | 2 | 2 | 25 | 2 | 13 - | 3 | 2 | 45 | 3 | 16 - | 4 | 2 | 17 | 1 | 17 -(8 rows) - -explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a),(b)) order by 3,1,2; - QUERY PLAN --------------------------------------------------------------------------------------------------------- - Sort - Sort Key: (GROUPING("*VALUES*".column1, "*VALUES*".column2)), "*VALUES*".column1, "*VALUES*".column2 - -> HashAggregate - Hash Key: "*VALUES*".column1 - Hash Key: "*VALUES*".column2 - -> Values Scan on "*VALUES*" -(6 rows) - -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by cube(a,b) order by 3,1,2; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - 1 | | 1 | 60 | 5 | 14 - 2 | | 1 | 15 | 1 | 15 - 3 | | 1 | 33 | 2 | 17 - 4 | | 1 | 37 | 2 | 19 - | 1 | 2 | 58 | 4 | 19 - | 2 | 2 | 25 | 2 | 13 - | 3 | 2 | 45 | 3 | 16 - | 4 | 2 | 17 | 1 | 17 - | | 3 | 145 | 10 | 19 -(16 rows) - -explain (costs off) select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by cube(a,b) order by 3,1,2; - QUERY PLAN --------------------------------------------------------------------------------------------------------- - Sort - Sort Key: (GROUPING("*VALUES*".column1, "*VALUES*".column2)), "*VALUES*".column1, "*VALUES*".column2 - -> MixedAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: "*VALUES*".column1 - Hash Key: "*VALUES*".column2 - Group Key: () - -> Values Scan on "*VALUES*" -(8 rows) - --- shouldn't try and hash -explain (costs off) - select a, b, grouping(a,b), array_agg(v order by v) - from gstest1 group by cube(a,b); - QUERY PLAN ----------------------------------------------------------- - GroupAggregate - Group Key: "*VALUES*".column1, "*VALUES*".column2 - Group Key: "*VALUES*".column1 - Group Key: () - Sort Key: "*VALUES*".column2 - Group Key: "*VALUES*".column2 - -> Sort - Sort Key: "*VALUES*".column1, "*VALUES*".column2 - -> Values Scan on "*VALUES*" -(9 rows) - --- unsortable cases -select unsortable_col, count(*) - from gstest4 group by grouping sets ((unsortable_col),(unsortable_col)) - order by unsortable_col::text; - unsortable_col | count -----------------+------- - 1 | 4 - 1 | 4 - 2 | 4 - 2 | 4 -(4 rows) - --- mixed hashable/sortable cases -select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((unhashable_col),(unsortable_col)) - order by 3, 5; - unhashable_col | unsortable_col | grouping | count | sum -----------------+----------------+----------+-------+----- - 0000 | | 1 | 2 | 17 - 0001 | | 1 | 2 | 34 - 0010 | | 1 | 2 | 68 - 0011 | | 1 | 2 | 136 - | 2 | 2 | 4 | 60 - | 1 | 2 | 4 | 195 -(6 rows) - -explain (costs off) - select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((unhashable_col),(unsortable_col)) - order by 3,5; - QUERY PLAN ------------------------------------------------------------------- - Sort - Sort Key: (GROUPING(unhashable_col, unsortable_col)), (sum(v)) - -> MixedAggregate - Hash Key: unsortable_col - Group Key: unhashable_col - -> Sort - Sort Key: unhashable_col - -> Seq Scan on gstest4 -(8 rows) - -select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((v,unhashable_col),(v,unsortable_col)) - order by 3,5; - unhashable_col | unsortable_col | grouping | count | sum -----------------+----------------+----------+-------+----- - 0000 | | 1 | 1 | 1 - 0001 | | 1 | 1 | 2 - 0010 | | 1 | 1 | 4 - 0011 | | 1 | 1 | 8 - 0000 | | 1 | 1 | 16 - 0001 | | 1 | 1 | 32 - 0010 | | 1 | 1 | 64 - 0011 | | 1 | 1 | 128 - | 1 | 2 | 1 | 1 - | 1 | 2 | 1 | 2 - | 2 | 2 | 1 | 4 - | 2 | 2 | 1 | 8 - | 2 | 2 | 1 | 16 - | 2 | 2 | 1 | 32 - | 1 | 2 | 1 | 64 - | 1 | 2 | 1 | 128 -(16 rows) - -explain (costs off) - select unhashable_col, unsortable_col, - grouping(unhashable_col, unsortable_col), - count(*), sum(v) - from gstest4 group by grouping sets ((v,unhashable_col),(v,unsortable_col)) - order by 3,5; - QUERY PLAN ------------------------------------------------------------------- - Sort - Sort Key: (GROUPING(unhashable_col, unsortable_col)), (sum(v)) - -> MixedAggregate - Hash Key: v, unsortable_col - Group Key: v, unhashable_col - -> Sort - Sort Key: v, unhashable_col - -> Seq Scan on gstest4 -(8 rows) - --- empty input: first is 0 rows, second 1, third 3 etc. -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); - a | b | sum | count ----+---+-----+------- -(0 rows) - -explain (costs off) - select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),a); - QUERY PLAN --------------------------------- - HashAggregate - Hash Key: a, b - Hash Key: a - -> Seq Scan on gstest_empty -(4 rows) - -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),()); - a | b | sum | count ----+---+-----+------- - | | | 0 -(1 row) - -select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); - a | b | sum | count ----+---+-----+------- - | | | 0 - | | | 0 - | | | 0 -(3 rows) - -explain (costs off) - select a, b, sum(v), count(*) from gstest_empty group by grouping sets ((a,b),(),(),()); - QUERY PLAN --------------------------------- - MixedAggregate - Hash Key: a, b - Group Key: () - Group Key: () - Group Key: () - -> Seq Scan on gstest_empty -(6 rows) - -select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); - sum | count ------+------- - | 0 - | 0 - | 0 -(3 rows) - -explain (costs off) - select sum(v), count(*) from gstest_empty group by grouping sets ((),(),()); - QUERY PLAN --------------------------------- - Aggregate - Group Key: () - Group Key: () - Group Key: () - -> Seq Scan on gstest_empty -(5 rows) - --- check that functionally dependent cols are not nulled -select a, d, grouping(a,b,c) - from gstest3 - group by grouping sets ((a,b), (a,c)); - a | d | grouping ----+---+---------- - 1 | 1 | 1 - 2 | 2 | 1 - 1 | 1 | 2 - 2 | 2 | 2 -(4 rows) - -explain (costs off) - select a, d, grouping(a,b,c) - from gstest3 - group by grouping sets ((a,b), (a,c)); - QUERY PLAN ---------------------------- - HashAggregate - Hash Key: a, b - Hash Key: a, c - -> Seq Scan on gstest3 -(4 rows) - --- simple rescan tests -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by grouping sets (a,b) - order by 1, 2, 3; - a | b | sum ----+---+----- - 1 | | 3 - 2 | | 6 - | 1 | 3 - | 2 | 3 - | 3 | 3 -(5 rows) - -explain (costs off) - select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by grouping sets (a,b) - order by 3, 1, 2; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: (sum("*VALUES*".column1)), gstest_data.a, gstest_data.b - -> HashAggregate - Hash Key: gstest_data.a - Hash Key: gstest_data.b - -> Nested Loop - -> Values Scan on "*VALUES*" - -> Function Scan on gstest_data -(8 rows) - -select * - from (values (1),(2)) v(x), - lateral (select a, b, sum(v.x) from gstest_data(v.x) group by grouping sets (a,b)) s; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 3: lateral (select a, b, sum(v.x) from gstest_data(v.x) ... - ^ -explain (costs off) - select * - from (values (1),(2)) v(x), - lateral (select a, b, sum(v.x) from gstest_data(v.x) group by grouping sets (a,b)) s; -ERROR: aggregate functions are not allowed in FROM clause of their own query level -LINE 4: lateral (select a, b, sum(v.x) from gstest_data(v.x... - ^ --- Tests for chained aggregates -select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; - a | b | grouping | sum | count | max ----+---+----------+-----+-------+----- - 1 | 1 | 0 | 21 | 2 | 11 - 1 | 2 | 0 | 25 | 2 | 13 - 1 | 3 | 0 | 14 | 1 | 14 - 2 | 3 | 0 | 15 | 1 | 15 - 3 | 3 | 0 | 16 | 1 | 16 - 3 | 4 | 0 | 17 | 1 | 17 - 4 | 1 | 0 | 37 | 2 | 19 - | | 3 | 21 | 2 | 11 - | | 3 | 21 | 2 | 11 - | | 3 | 25 | 2 | 13 - | | 3 | 25 | 2 | 13 - | | 3 | 14 | 1 | 14 - | | 3 | 14 | 1 | 14 - | | 3 | 15 | 1 | 15 - | | 3 | 15 | 1 | 15 - | | 3 | 16 | 1 | 16 - | | 3 | 16 | 1 | 16 - | | 3 | 17 | 1 | 17 - | | 3 | 17 | 1 | 17 - | | 3 | 37 | 2 | 19 - | | 3 | 37 | 2 | 19 -(21 rows) - -explain (costs off) - select a, b, grouping(a,b), sum(v), count(*), max(v) - from gstest1 group by grouping sets ((a,b),(a+1,b+1),(a+2,b+2)) order by 3,6; - QUERY PLAN -------------------------------------------------------------------------------------------- - Sort - Sort Key: (GROUPING("*VALUES*".column1, "*VALUES*".column2)), (max("*VALUES*".column3)) - -> HashAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: ("*VALUES*".column1 + 1), ("*VALUES*".column2 + 1) - Hash Key: ("*VALUES*".column1 + 2), ("*VALUES*".column2 + 2) - -> Values Scan on "*VALUES*" -(7 rows) - -select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by cube (a,b) order by rsum, a, b; - a | b | sum | rsum ----+---+-----+------ - 1 | 1 | 8 | 8 - 1 | 2 | 2 | 10 - 1 | | 10 | 20 - 2 | 2 | 2 | 22 - 2 | | 2 | 24 - | 1 | 8 | 32 - | 2 | 4 | 36 - | | 12 | 48 -(8 rows) - -explain (costs off) - select a, b, sum(c), sum(sum(c)) over (order by a,b) as rsum - from gstest2 group by cube (a,b) order by rsum, a, b; - QUERY PLAN ---------------------------------------------- - Sort - Sort Key: (sum((sum(c))) OVER w1), a, b - -> WindowAgg - Window: w1 AS (ORDER BY a, b) - -> Sort - Sort Key: a, b - -> MixedAggregate - Hash Key: a, b - Hash Key: a - Hash Key: b - Group Key: () - -> Seq Scan on gstest2 -(12 rows) - -select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by cube (a,b) order by a,b; - a | b | sum ----+---+----- - 1 | 1 | 1 - 1 | 2 | 1 - 1 | 3 | 1 - 1 | | 3 - 2 | 1 | 2 - 2 | 2 | 2 - 2 | 3 | 2 - 2 | | 6 - | 1 | 3 - | 2 | 3 - | 3 | 3 - | | 9 -(12 rows) - -explain (costs off) - select a, b, sum(v.x) - from (values (1),(2)) v(x), gstest_data(v.x) - group by cube (a,b) order by a,b; - QUERY PLAN ------------------------------------------------- - Sort - Sort Key: gstest_data.a, gstest_data.b - -> MixedAggregate - Hash Key: gstest_data.a, gstest_data.b - Hash Key: gstest_data.a - Hash Key: gstest_data.b - Group Key: () - -> Nested Loop - -> Values Scan on "*VALUES*" - -> Function Scan on gstest_data -(10 rows) - --- Verify that we correctly handle the child node returning a --- non-minimal slot, which happens if the input is pre-sorted, --- e.g. due to an index scan. -BEGIN; -SET LOCAL enable_hashagg = false; -EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - QUERY PLAN ---------------------------------------- - Sort - Sort Key: a, b - -> GroupAggregate - Group Key: a - Group Key: () - Sort Key: b - Group Key: b - -> Sort - Sort Key: a - -> Seq Scan on gstest3 -(10 rows) - -SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - a | b | count | max | max ----+---+-------+-----+----- - 1 | | 1 | 1 | 1 - 2 | | 1 | 2 | 2 - | 1 | 1 | 1 | 1 - | 2 | 1 | 2 | 2 - | | 2 | 2 | 2 -(5 rows) - -SET LOCAL enable_seqscan = false; -EXPLAIN (COSTS OFF) SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - QUERY PLAN ------------------------------------------------------- - Sort - Sort Key: a, b - -> GroupAggregate - Group Key: a - Group Key: () - Sort Key: b - Group Key: b - -> Index Scan using gstest3_pkey on gstest3 -(8 rows) - -SELECT a, b, count(*), max(a), max(b) FROM gstest3 GROUP BY GROUPING SETS(a, b,()) ORDER BY a, b; - a | b | count | max | max ----+---+-------+-----+----- - 1 | | 1 | 1 | 1 - 2 | | 1 | 2 | 2 - | 1 | 1 | 1 | 1 - | 2 | 1 | 2 | 2 - | | 2 | 2 | 2 -(5 rows) - -COMMIT; --- More rescan tests -select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by cube(four,ten)) s on true order by v.a,four,ten; - a | a | four | ten | count ----+---+------+-----+------- - 1 | 1 | 0 | 0 | 50 - 1 | 1 | 0 | 2 | 50 - 1 | 1 | 0 | 4 | 50 - 1 | 1 | 0 | 6 | 50 - 1 | 1 | 0 | 8 | 50 - 1 | 1 | 0 | | 250 - 1 | 1 | 1 | 1 | 50 - 1 | 1 | 1 | 3 | 50 - 1 | 1 | 1 | 5 | 50 - 1 | 1 | 1 | 7 | 50 - 1 | 1 | 1 | 9 | 50 - 1 | 1 | 1 | | 250 - 1 | 1 | 2 | 0 | 50 - 1 | 1 | 2 | 2 | 50 - 1 | 1 | 2 | 4 | 50 - 1 | 1 | 2 | 6 | 50 - 1 | 1 | 2 | 8 | 50 - 1 | 1 | 2 | | 250 - 1 | 1 | 3 | 1 | 50 - 1 | 1 | 3 | 3 | 50 - 1 | 1 | 3 | 5 | 50 - 1 | 1 | 3 | 7 | 50 - 1 | 1 | 3 | 9 | 50 - 1 | 1 | 3 | | 250 - 1 | 1 | | 0 | 100 - 1 | 1 | | 1 | 100 - 1 | 1 | | 2 | 100 - 1 | 1 | | 3 | 100 - 1 | 1 | | 4 | 100 - 1 | 1 | | 5 | 100 - 1 | 1 | | 6 | 100 - 1 | 1 | | 7 | 100 - 1 | 1 | | 8 | 100 - 1 | 1 | | 9 | 100 - 1 | 1 | | | 1000 - 2 | 2 | 0 | 0 | 50 - 2 | 2 | 0 | 2 | 50 - 2 | 2 | 0 | 4 | 50 - 2 | 2 | 0 | 6 | 50 - 2 | 2 | 0 | 8 | 50 - 2 | 2 | 0 | | 250 - 2 | 2 | 1 | 1 | 50 - 2 | 2 | 1 | 3 | 50 - 2 | 2 | 1 | 5 | 50 - 2 | 2 | 1 | 7 | 50 - 2 | 2 | 1 | 9 | 50 - 2 | 2 | 1 | | 250 - 2 | 2 | 2 | 0 | 50 - 2 | 2 | 2 | 2 | 50 - 2 | 2 | 2 | 4 | 50 - 2 | 2 | 2 | 6 | 50 - 2 | 2 | 2 | 8 | 50 - 2 | 2 | 2 | | 250 - 2 | 2 | 3 | 1 | 50 - 2 | 2 | 3 | 3 | 50 - 2 | 2 | 3 | 5 | 50 - 2 | 2 | 3 | 7 | 50 - 2 | 2 | 3 | 9 | 50 - 2 | 2 | 3 | | 250 - 2 | 2 | | 0 | 100 - 2 | 2 | | 1 | 100 - 2 | 2 | | 2 | 100 - 2 | 2 | | 3 | 100 - 2 | 2 | | 4 | 100 - 2 | 2 | | 5 | 100 - 2 | 2 | | 6 | 100 - 2 | 2 | | 7 | 100 - 2 | 2 | | 8 | 100 - 2 | 2 | | 9 | 100 - 2 | 2 | | | 1000 -(70 rows) - -select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by cube(two,four) order by two,four) s1) from (values (1),(2)) v(a); - array ------------------------------------------------------------------------------------------------------------------------------------------------------- - {"(1,0,0,250)","(1,0,2,250)","(1,0,,500)","(1,1,1,250)","(1,1,3,250)","(1,1,,500)","(1,,0,250)","(1,,1,250)","(1,,2,250)","(1,,3,250)","(1,,,1000)"} - {"(2,0,0,250)","(2,0,2,250)","(2,0,,500)","(2,1,1,250)","(2,1,3,250)","(2,1,,500)","(2,,0,250)","(2,,1,250)","(2,,2,250)","(2,,3,250)","(2,,,1000)"} -(2 rows) - --- Rescan logic changes when there are no empty grouping sets, so test --- that too: -select * from (values (1),(2)) v(a) left join lateral (select v.a, four, ten, count(*) from onek group by grouping sets(four,ten)) s on true order by v.a,four,ten; - a | a | four | ten | count ----+---+------+-----+------- - 1 | 1 | 0 | | 250 - 1 | 1 | 1 | | 250 - 1 | 1 | 2 | | 250 - 1 | 1 | 3 | | 250 - 1 | 1 | | 0 | 100 - 1 | 1 | | 1 | 100 - 1 | 1 | | 2 | 100 - 1 | 1 | | 3 | 100 - 1 | 1 | | 4 | 100 - 1 | 1 | | 5 | 100 - 1 | 1 | | 6 | 100 - 1 | 1 | | 7 | 100 - 1 | 1 | | 8 | 100 - 1 | 1 | | 9 | 100 - 2 | 2 | 0 | | 250 - 2 | 2 | 1 | | 250 - 2 | 2 | 2 | | 250 - 2 | 2 | 3 | | 250 - 2 | 2 | | 0 | 100 - 2 | 2 | | 1 | 100 - 2 | 2 | | 2 | 100 - 2 | 2 | | 3 | 100 - 2 | 2 | | 4 | 100 - 2 | 2 | | 5 | 100 - 2 | 2 | | 6 | 100 - 2 | 2 | | 7 | 100 - 2 | 2 | | 8 | 100 - 2 | 2 | | 9 | 100 -(28 rows) - -select array(select row(v.a,s1.*) from (select two,four, count(*) from onek group by grouping sets(two,four) order by two,four) s1) from (values (1),(2)) v(a); - array ---------------------------------------------------------------------------------- - {"(1,0,,500)","(1,1,,500)","(1,,0,250)","(1,,1,250)","(1,,2,250)","(1,,3,250)"} - {"(2,0,,500)","(2,1,,500)","(2,,0,250)","(2,,1,250)","(2,,2,250)","(2,,3,250)"} -(2 rows) - --- test the knapsack -set enable_indexscan = false; -set hash_mem_multiplier = 1.0; -set work_mem = '64kB'; -explain (costs off) - select unique1, - count(two), count(four), count(ten), - count(hundred), count(thousand), count(twothousand), - count(*) - from tenk1 group by grouping sets (unique1,twothousand,thousand,hundred,ten,four,two); - QUERY PLAN -------------------------------- - MixedAggregate - Hash Key: two - Hash Key: four - Hash Key: ten - Hash Key: hundred - Group Key: unique1 - Sort Key: twothousand - Group Key: twothousand - Sort Key: thousand - Group Key: thousand - -> Sort - Sort Key: unique1 - -> Seq Scan on tenk1 -(13 rows) - -explain (costs off) - select unique1, - count(two), count(four), count(ten), - count(hundred), count(thousand), count(twothousand), - count(*) - from tenk1 group by grouping sets (unique1,hundred,ten,four,two); - QUERY PLAN -------------------------------- - MixedAggregate - Hash Key: two - Hash Key: four - Hash Key: ten - Hash Key: hundred - Group Key: unique1 - -> Sort - Sort Key: unique1 - -> Seq Scan on tenk1 -(9 rows) - -set work_mem = '384kB'; -explain (costs off) - select unique1, - count(two), count(four), count(ten), - count(hundred), count(thousand), count(twothousand), - count(*) - from tenk1 group by grouping sets (unique1,twothousand,thousand,hundred,ten,four,two); - QUERY PLAN -------------------------------- - MixedAggregate - Hash Key: two - Hash Key: four - Hash Key: ten - Hash Key: hundred - Hash Key: thousand - Group Key: unique1 - Sort Key: twothousand - Group Key: twothousand - -> Sort - Sort Key: unique1 - -> Seq Scan on tenk1 -(12 rows) - --- check collation-sensitive matching between grouping expressions --- (similar to a check for aggregates, but there are additional code --- paths for GROUPING, so check again here) -select v||'a', case grouping(v||'a') when 1 then 1 else 0 end, count(*) - from unnest(array[1,1], array['a','b']) u(i,v) - group by rollup(i, v||'a') order by 1,3; - ?column? | case | count -----------+------+------- - aa | 0 | 1 - ba | 0 | 1 - | 1 | 2 - | 1 | 2 -(4 rows) - -select v||'a', case when grouping(v||'a') = 1 then 1 else 0 end, count(*) - from unnest(array[1,1], array['a','b']) u(i,v) - group by rollup(i, v||'a') order by 1,3; - ?column? | case | count -----------+------+------- - aa | 0 | 1 - ba | 0 | 1 - | 1 | 2 - | 1 | 2 -(4 rows) - --- Bug #16784 -create table bug_16784(i int, j int); -analyze bug_16784; -alter table bug_16784 set (autovacuum_enabled = 'false'); -update pg_class set reltuples = 10 where relname='bug_16784'; -insert into bug_16784 select g/10, g from generate_series(1,40) g; -set work_mem='64kB'; -set enable_sort = false; -select * from - (values (1),(2)) v(a), - lateral (select a, i, j, count(*) from - bug_16784 group by cube(i,j)) s - order by v.a, i, j; - a | a | i | j | count ----+---+---+----+------- - 1 | 1 | 0 | 1 | 1 - 1 | 1 | 0 | 2 | 1 - 1 | 1 | 0 | 3 | 1 - 1 | 1 | 0 | 4 | 1 - 1 | 1 | 0 | 5 | 1 - 1 | 1 | 0 | 6 | 1 - 1 | 1 | 0 | 7 | 1 - 1 | 1 | 0 | 8 | 1 - 1 | 1 | 0 | 9 | 1 - 1 | 1 | 0 | | 9 - 1 | 1 | 1 | 10 | 1 - 1 | 1 | 1 | 11 | 1 - 1 | 1 | 1 | 12 | 1 - 1 | 1 | 1 | 13 | 1 - 1 | 1 | 1 | 14 | 1 - 1 | 1 | 1 | 15 | 1 - 1 | 1 | 1 | 16 | 1 - 1 | 1 | 1 | 17 | 1 - 1 | 1 | 1 | 18 | 1 - 1 | 1 | 1 | 19 | 1 - 1 | 1 | 1 | | 10 - 1 | 1 | 2 | 20 | 1 - 1 | 1 | 2 | 21 | 1 - 1 | 1 | 2 | 22 | 1 - 1 | 1 | 2 | 23 | 1 - 1 | 1 | 2 | 24 | 1 - 1 | 1 | 2 | 25 | 1 - 1 | 1 | 2 | 26 | 1 - 1 | 1 | 2 | 27 | 1 - 1 | 1 | 2 | 28 | 1 - 1 | 1 | 2 | 29 | 1 - 1 | 1 | 2 | | 10 - 1 | 1 | 3 | 30 | 1 - 1 | 1 | 3 | 31 | 1 - 1 | 1 | 3 | 32 | 1 - 1 | 1 | 3 | 33 | 1 - 1 | 1 | 3 | 34 | 1 - 1 | 1 | 3 | 35 | 1 - 1 | 1 | 3 | 36 | 1 - 1 | 1 | 3 | 37 | 1 - 1 | 1 | 3 | 38 | 1 - 1 | 1 | 3 | 39 | 1 - 1 | 1 | 3 | | 10 - 1 | 1 | 4 | 40 | 1 - 1 | 1 | 4 | | 1 - 1 | 1 | | 1 | 1 - 1 | 1 | | 2 | 1 - 1 | 1 | | 3 | 1 - 1 | 1 | | 4 | 1 - 1 | 1 | | 5 | 1 - 1 | 1 | | 6 | 1 - 1 | 1 | | 7 | 1 - 1 | 1 | | 8 | 1 - 1 | 1 | | 9 | 1 - 1 | 1 | | 10 | 1 - 1 | 1 | | 11 | 1 - 1 | 1 | | 12 | 1 - 1 | 1 | | 13 | 1 - 1 | 1 | | 14 | 1 - 1 | 1 | | 15 | 1 - 1 | 1 | | 16 | 1 - 1 | 1 | | 17 | 1 - 1 | 1 | | 18 | 1 - 1 | 1 | | 19 | 1 - 1 | 1 | | 20 | 1 - 1 | 1 | | 21 | 1 - 1 | 1 | | 22 | 1 - 1 | 1 | | 23 | 1 - 1 | 1 | | 24 | 1 - 1 | 1 | | 25 | 1 - 1 | 1 | | 26 | 1 - 1 | 1 | | 27 | 1 - 1 | 1 | | 28 | 1 - 1 | 1 | | 29 | 1 - 1 | 1 | | 30 | 1 - 1 | 1 | | 31 | 1 - 1 | 1 | | 32 | 1 - 1 | 1 | | 33 | 1 - 1 | 1 | | 34 | 1 - 1 | 1 | | 35 | 1 - 1 | 1 | | 36 | 1 - 1 | 1 | | 37 | 1 - 1 | 1 | | 38 | 1 - 1 | 1 | | 39 | 1 - 1 | 1 | | 40 | 1 - 1 | 1 | | | 40 - 2 | 2 | 0 | 1 | 1 - 2 | 2 | 0 | 2 | 1 - 2 | 2 | 0 | 3 | 1 - 2 | 2 | 0 | 4 | 1 - 2 | 2 | 0 | 5 | 1 - 2 | 2 | 0 | 6 | 1 - 2 | 2 | 0 | 7 | 1 - 2 | 2 | 0 | 8 | 1 - 2 | 2 | 0 | 9 | 1 - 2 | 2 | 0 | | 9 - 2 | 2 | 1 | 10 | 1 - 2 | 2 | 1 | 11 | 1 - 2 | 2 | 1 | 12 | 1 - 2 | 2 | 1 | 13 | 1 - 2 | 2 | 1 | 14 | 1 - 2 | 2 | 1 | 15 | 1 - 2 | 2 | 1 | 16 | 1 - 2 | 2 | 1 | 17 | 1 - 2 | 2 | 1 | 18 | 1 - 2 | 2 | 1 | 19 | 1 - 2 | 2 | 1 | | 10 - 2 | 2 | 2 | 20 | 1 - 2 | 2 | 2 | 21 | 1 - 2 | 2 | 2 | 22 | 1 - 2 | 2 | 2 | 23 | 1 - 2 | 2 | 2 | 24 | 1 - 2 | 2 | 2 | 25 | 1 - 2 | 2 | 2 | 26 | 1 - 2 | 2 | 2 | 27 | 1 - 2 | 2 | 2 | 28 | 1 - 2 | 2 | 2 | 29 | 1 - 2 | 2 | 2 | | 10 - 2 | 2 | 3 | 30 | 1 - 2 | 2 | 3 | 31 | 1 - 2 | 2 | 3 | 32 | 1 - 2 | 2 | 3 | 33 | 1 - 2 | 2 | 3 | 34 | 1 - 2 | 2 | 3 | 35 | 1 - 2 | 2 | 3 | 36 | 1 - 2 | 2 | 3 | 37 | 1 - 2 | 2 | 3 | 38 | 1 - 2 | 2 | 3 | 39 | 1 - 2 | 2 | 3 | | 10 - 2 | 2 | 4 | 40 | 1 - 2 | 2 | 4 | | 1 - 2 | 2 | | 1 | 1 - 2 | 2 | | 2 | 1 - 2 | 2 | | 3 | 1 - 2 | 2 | | 4 | 1 - 2 | 2 | | 5 | 1 - 2 | 2 | | 6 | 1 - 2 | 2 | | 7 | 1 - 2 | 2 | | 8 | 1 - 2 | 2 | | 9 | 1 - 2 | 2 | | 10 | 1 - 2 | 2 | | 11 | 1 - 2 | 2 | | 12 | 1 - 2 | 2 | | 13 | 1 - 2 | 2 | | 14 | 1 - 2 | 2 | | 15 | 1 - 2 | 2 | | 16 | 1 - 2 | 2 | | 17 | 1 - 2 | 2 | | 18 | 1 - 2 | 2 | | 19 | 1 - 2 | 2 | | 20 | 1 - 2 | 2 | | 21 | 1 - 2 | 2 | | 22 | 1 - 2 | 2 | | 23 | 1 - 2 | 2 | | 24 | 1 - 2 | 2 | | 25 | 1 - 2 | 2 | | 26 | 1 - 2 | 2 | | 27 | 1 - 2 | 2 | | 28 | 1 - 2 | 2 | | 29 | 1 - 2 | 2 | | 30 | 1 - 2 | 2 | | 31 | 1 - 2 | 2 | | 32 | 1 - 2 | 2 | | 33 | 1 - 2 | 2 | | 34 | 1 - 2 | 2 | | 35 | 1 - 2 | 2 | | 36 | 1 - 2 | 2 | | 37 | 1 - 2 | 2 | | 38 | 1 - 2 | 2 | | 39 | 1 - 2 | 2 | | 40 | 1 - 2 | 2 | | | 40 -(172 rows) - --- --- Compare results between plans using sorting and plans using hash --- aggregation. Force spilling in both cases by setting work_mem low --- and altering the statistics. --- -create table gs_data_1 as -select g%1000 as g1000, g%100 as g100, g%10 as g10, g - from generate_series(0,1999) g; -analyze gs_data_1; -alter table gs_data_1 set (autovacuum_enabled = 'false'); -update pg_class set reltuples = 10 where relname='gs_data_1'; -set work_mem='64kB'; --- Produce results with sorting. -set enable_sort = true; -set enable_hashagg = false; -set jit_above_cost = 0; -explain (costs off) -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); - QUERY PLAN ------------------------------------- - GroupAggregate - Group Key: g1000, g100, g10 - Group Key: g1000, g100 - Group Key: g1000 - Group Key: () - Sort Key: g100, g10 - Group Key: g100, g10 - Group Key: g100 - Sort Key: g10, g1000 - Group Key: g10, g1000 - Group Key: g10 - -> Sort - Sort Key: g1000, g100, g10 - -> Seq Scan on gs_data_1 -(14 rows) - -create table gs_group_1 as -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); --- Produce results with hash aggregation. -set enable_hashagg = true; -set enable_sort = false; -explain (costs off) -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); - QUERY PLAN ------------------------------- - MixedAggregate - Hash Key: g1000, g100, g10 - Hash Key: g1000, g100 - Hash Key: g1000 - Hash Key: g100, g10 - Hash Key: g100 - Hash Key: g10, g1000 - Hash Key: g10 - Group Key: () - -> Seq Scan on gs_data_1 -(10 rows) - -create table gs_hash_1 as -select g100, g10, sum(g::numeric), count(*), max(g::text) -from gs_data_1 group by cube (g1000, g100,g10); -set enable_sort = true; -set work_mem to default; -set hash_mem_multiplier to default; --- Compare results -(select * from gs_hash_1 except select * from gs_group_1) - union all -(select * from gs_group_1 except select * from gs_hash_1); - g100 | g10 | sum | count | max -------+-----+-----+-------+----- -(0 rows) - -drop table gs_group_1; -drop table gs_hash_1; --- GROUP BY DISTINCT --- "normal" behavior... -select a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by all rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | 2 | - 1 | | 3 - 1 | | 3 - 1 | | - 1 | | - 1 | | - 4 | | 6 - 4 | | 6 - 4 | | 6 - 4 | | - 4 | | - 4 | | - 4 | | - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | 8 | - 7 | | 9 - 7 | | 9 - 7 | | - 7 | | - 7 | | - | | -(25 rows) - --- ...which is also the default -select a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | 2 | - 1 | | 3 - 1 | | 3 - 1 | | - 1 | | - 1 | | - 4 | | 6 - 4 | | 6 - 4 | | 6 - 4 | | - 4 | | - 4 | | - 4 | | - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | 8 | - 7 | | 9 - 7 | | 9 - 7 | | - 7 | | - 7 | | - | | -(25 rows) - --- "group by distinct" behavior... -select a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by distinct rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | | 3 - 1 | | - 4 | | 6 - 4 | | 6 - 4 | | - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | | 9 - 7 | | - | | -(13 rows) - --- ...which is not the same as "select distinct" -select distinct a, b, c -from (values (1, 2, 3), (4, null, 6), (7, 8, 9)) as t (a, b, c) -group by rollup(a, b), rollup(a, c) -order by a, b, c; - a | b | c ----+---+--- - 1 | 2 | 3 - 1 | 2 | - 1 | | 3 - 1 | | - 4 | | 6 - 4 | | - 7 | 8 | 9 - 7 | 8 | - 7 | | 9 - 7 | | - | | -(11 rows) - --- test handling of outer GroupingFunc within subqueries -explain (costs off) -select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); - QUERY PLAN ------------------------------------- - MixedAggregate - Hash Key: (InitPlan expr_3).col1 - Group Key: () - InitPlan expr_2 - -> Result - InitPlan expr_3 - -> Result - -> Result - SubPlan expr_1 - -> Result -(10 rows) - -select (select grouping(v1)) from (values ((select 1))) v(v1) group by cube(v1); - grouping ----------- - 1 - 0 -(2 rows) - -explain (costs off) -select (select grouping(v1)) from (values ((select 1))) v(v1) group by v1; - QUERY PLAN -------------------- - GroupAggregate - InitPlan expr_2 - -> Result - InitPlan expr_3 - -> Result - -> Result - SubPlan expr_1 - -> Result -(8 rows) - -select (select grouping(v1)) from (values ((select 1))) v(v1) group by v1; - grouping ----------- - 0 -(1 row) - --- test handling of subqueries in grouping sets -create temp table gstest5(id integer primary key, v integer); -insert into gstest5 select i, i from generate_series(1,5)i; -explain (verbose, costs off) -select grouping((select t1.v from gstest5 t2 where id = t1.id)), - (select t1.v from gstest5 t2 where id = t1.id) as s -from gstest5 t1 -group by grouping sets(v, s) -order by case when grouping((select t1.v from gstest5 t2 where id = t1.id)) = 0 - then (select t1.v from gstest5 t2 where id = t1.id) - else null end - nulls first; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Sort - Output: (GROUPING((SubPlan expr_1))), ((SubPlan expr_3)), (CASE WHEN (GROUPING((SubPlan expr_2)) = 0) THEN ((SubPlan expr_3)) ELSE NULL::integer END), t1.v - Sort Key: (CASE WHEN (GROUPING((SubPlan expr_2)) = 0) THEN ((SubPlan expr_3)) ELSE NULL::integer END) NULLS FIRST - -> HashAggregate - Output: GROUPING((SubPlan expr_1)), ((SubPlan expr_3)), CASE WHEN (GROUPING((SubPlan expr_2)) = 0) THEN ((SubPlan expr_3)) ELSE NULL::integer END, t1.v - Hash Key: t1.v - Hash Key: (SubPlan expr_3) - -> Seq Scan on pg_temp.gstest5 t1 - Output: (SubPlan expr_3), t1.v, t1.id - SubPlan expr_3 - -> Bitmap Heap Scan on pg_temp.gstest5 t2 - Output: t1.v - Recheck Cond: (t2.id = t1.id) - -> Bitmap Index Scan on gstest5_pkey - Index Cond: (t2.id = t1.id) -(15 rows) - -select grouping((select t1.v from gstest5 t2 where id = t1.id)), - (select t1.v from gstest5 t2 where id = t1.id) as s -from gstest5 t1 -group by grouping sets(v, s) -order by case when grouping((select t1.v from gstest5 t2 where id = t1.id)) = 0 - then (select t1.v from gstest5 t2 where id = t1.id) - else null end - nulls first; - grouping | s -----------+--- - 1 | - 1 | - 1 | - 1 | - 1 | - 0 | 1 - 0 | 2 - 0 | 3 - 0 | 4 - 0 | 5 -(10 rows) - -explain (verbose, costs off) -select grouping((select t1.v from gstest5 t2 where id = t1.id)), - (select t1.v from gstest5 t2 where id = t1.id) as s, - case when grouping((select t1.v from gstest5 t2 where id = t1.id)) = 0 - then (select t1.v from gstest5 t2 where id = t1.id) - else null end as o -from gstest5 t1 -group by grouping sets(v, s) -order by o nulls first; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------ - Sort - Output: (GROUPING((SubPlan expr_1))), ((SubPlan expr_3)), (CASE WHEN (GROUPING((SubPlan expr_2)) = 0) THEN ((SubPlan expr_3)) ELSE NULL::integer END), t1.v - Sort Key: (CASE WHEN (GROUPING((SubPlan expr_2)) = 0) THEN ((SubPlan expr_3)) ELSE NULL::integer END) NULLS FIRST - -> HashAggregate - Output: GROUPING((SubPlan expr_1)), ((SubPlan expr_3)), CASE WHEN (GROUPING((SubPlan expr_2)) = 0) THEN ((SubPlan expr_3)) ELSE NULL::integer END, t1.v - Hash Key: t1.v - Hash Key: (SubPlan expr_3) - -> Seq Scan on pg_temp.gstest5 t1 - Output: (SubPlan expr_3), t1.v, t1.id - SubPlan expr_3 - -> Bitmap Heap Scan on pg_temp.gstest5 t2 - Output: t1.v - Recheck Cond: (t2.id = t1.id) - -> Bitmap Index Scan on gstest5_pkey - Index Cond: (t2.id = t1.id) -(15 rows) - -select grouping((select t1.v from gstest5 t2 where id = t1.id)), - (select t1.v from gstest5 t2 where id = t1.id) as s, - case when grouping((select t1.v from gstest5 t2 where id = t1.id)) = 0 - then (select t1.v from gstest5 t2 where id = t1.id) - else null end as o -from gstest5 t1 -group by grouping sets(v, s) -order by o nulls first; - grouping | s | o -----------+---+--- - 1 | | - 1 | | - 1 | | - 1 | | - 1 | | - 0 | 1 | 1 - 0 | 2 | 2 - 0 | 3 | 3 - 0 | 4 | 4 - 0 | 5 | 5 -(10 rows) - --- test handling of expressions that should match lower target items -explain (costs off) -select a < b and b < 3 from (values (1, 2)) t(a, b) group by rollup(a < b and b < 3) having a < b and b < 3; - QUERY PLAN ------------------------------------ - MixedAggregate - Hash Key: ((1 < 2) AND (2 < 3)) - Group Key: () - Filter: (((1 < 2) AND (2 < 3))) - -> Result -(5 rows) - -select a < b and b < 3 from (values (1, 2)) t(a, b) group by rollup(a < b and b < 3) having a < b and b < 3; - ?column? ----------- - t -(1 row) - -explain (costs off) -select not a from (values(true)) t(a) group by rollup(not a) having not not a; - QUERY PLAN ------------------------------- - MixedAggregate - Hash Key: (NOT true) - Group Key: () - Filter: (NOT ((NOT true))) - -> Result -(5 rows) - -select not a from (values(true)) t(a) group by rollup(not a) having not not a; - ?column? ----------- - f -(1 row) - --- test handling of expressions nullable by grouping sets -explain (costs off) -select distinct on (a, b) a, b -from (values (1, 1), (2, 2)) as t (a, b) where a = b -group by grouping sets((a, b), (a)) -order by a, b; - QUERY PLAN ----------------------------------------------------------------- - Unique - -> Sort - Sort Key: "*VALUES*".column1, "*VALUES*".column2 - -> HashAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: "*VALUES*".column1 - -> Values Scan on "*VALUES*" - Filter: (column1 = column2) -(8 rows) - -select distinct on (a, b) a, b -from (values (1, 1), (2, 2)) as t (a, b) where a = b -group by grouping sets((a, b), (a)) -order by a, b; - a | b ----+--- - 1 | 1 - 1 | - 2 | 2 - 2 | -(4 rows) - -explain (costs off) -select distinct on (a, b+1) a, b+1 -from (values (1, 0), (2, 1)) as t (a, b) where a = b+1 -group by grouping sets((a, b+1), (a)) -order by a, b+1; - QUERY PLAN ----------------------------------------------------------------------- - Unique - -> Sort - Sort Key: "*VALUES*".column1, (("*VALUES*".column2 + 1)) - -> HashAggregate - Hash Key: "*VALUES*".column1, ("*VALUES*".column2 + 1) - Hash Key: "*VALUES*".column1 - -> Values Scan on "*VALUES*" - Filter: (column1 = (column2 + 1)) -(8 rows) - -select distinct on (a, b+1) a, b+1 -from (values (1, 0), (2, 1)) as t (a, b) where a = b+1 -group by grouping sets((a, b+1), (a)) -order by a, b+1; - a | ?column? ----+---------- - 1 | 1 - 1 | - 2 | 2 - 2 | -(4 rows) - -explain (costs off) -select a, b -from (values (1, 1), (2, 2)) as t (a, b) where a = b -group by grouping sets((a, b), (a)) -order by a, b nulls first; - QUERY PLAN ----------------------------------------------------------------- - Sort - Sort Key: "*VALUES*".column1, "*VALUES*".column2 NULLS FIRST - -> HashAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: "*VALUES*".column1 - -> Values Scan on "*VALUES*" - Filter: (column1 = column2) -(7 rows) - -select a, b -from (values (1, 1), (2, 2)) as t (a, b) where a = b -group by grouping sets((a, b), (a)) -order by a, b nulls first; - a | b ----+--- - 1 | - 1 | 1 - 2 | - 2 | 2 -(4 rows) - -explain (costs off) -select 1 as one group by rollup(one) order by one nulls first; - QUERY PLAN ------------------------------ - Sort - Sort Key: (1) NULLS FIRST - -> MixedAggregate - Hash Key: 1 - Group Key: () - -> Result -(6 rows) - -select 1 as one group by rollup(one) order by one nulls first; - one ------ - - 1 -(2 rows) - -explain (costs off) -select a, b, row_number() over (order by a, b nulls first) -from (values (1, 1), (2, 2)) as t (a, b) where a = b -group by grouping sets((a, b), (a)); - QUERY PLAN --------------------------------------------------------------------------------------------- - WindowAgg - Window: w1 AS (ORDER BY "*VALUES*".column1, "*VALUES*".column2 ROWS UNBOUNDED PRECEDING) - -> Sort - Sort Key: "*VALUES*".column1, "*VALUES*".column2 NULLS FIRST - -> HashAggregate - Hash Key: "*VALUES*".column1, "*VALUES*".column2 - Hash Key: "*VALUES*".column1 - -> Values Scan on "*VALUES*" - Filter: (column1 = column2) -(9 rows) - -select a, b, row_number() over (order by a, b nulls first) -from (values (1, 1), (2, 2)) as t (a, b) where a = b -group by grouping sets((a, b), (a)); - a | b | row_number ----+---+------------ - 1 | | 1 - 1 | 1 | 2 - 2 | | 3 - 2 | 2 | 4 -(4 rows) - --- test handling of SRFs with grouping sets -explain (verbose, costs off) -select generate_series(1, a) as g -from (values (1, 1), (2, 2)) as t (a, b) -group by rollup(g) -order by 1; - QUERY PLAN --------------------------------------------------------------- - Sort - Output: (generate_series(1, "*VALUES*".column1)) - Sort Key: (generate_series(1, "*VALUES*".column1)) - -> MixedAggregate - Output: (generate_series(1, "*VALUES*".column1)) - Hash Key: generate_series(1, "*VALUES*".column1) - Group Key: () - -> ProjectSet - Output: generate_series(1, "*VALUES*".column1) - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 -(11 rows) - -select generate_series(1, a) as g -from (values (1, 1), (2, 2)) as t (a, b) -group by rollup(g) -order by 1; - g ---- - 1 - 2 - -(3 rows) - -explain (verbose, costs off) -select generate_series(1, a) as g, a+b as ab -from (values (1, 1), (2, 2)) as t (a, b) -group by rollup(a, ab) -order by 1, 2; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------- - Sort - Output: (generate_series(1, "*VALUES*".column1)), (("*VALUES*".column1 + "*VALUES*".column2)), "*VALUES*".column1 - Sort Key: (generate_series(1, "*VALUES*".column1)), (("*VALUES*".column1 + "*VALUES*".column2)) - -> ProjectSet - Output: generate_series(1, "*VALUES*".column1), (("*VALUES*".column1 + "*VALUES*".column2)), "*VALUES*".column1 - -> MixedAggregate - Output: "*VALUES*".column1, (("*VALUES*".column1 + "*VALUES*".column2)) - Hash Key: "*VALUES*".column1, ("*VALUES*".column1 + "*VALUES*".column2) - Hash Key: "*VALUES*".column1 - Group Key: () - -> Values Scan on "*VALUES*" - Output: ("*VALUES*".column1 + "*VALUES*".column2), "*VALUES*".column1 -(12 rows) - -select generate_series(1, a) as g, a+b as ab -from (values (1, 1), (2, 2)) as t (a, b) -group by rollup(a, ab) -order by 1, 2; - g | ab ----+---- - 1 | 2 - 1 | 4 - 1 | - 1 | - 2 | 4 - 2 | -(6 rows) - --- end +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/collate.icu.utf8_1.out /tmp/cirrus-ci-build/src/test/regress/results/collate.icu.utf8.out --- /tmp/cirrus-ci-build/src/test/regress/expected/collate.icu.utf8_1.out 2026-03-09 20:33:45.132311473 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/collate.icu.utf8.out 2026-03-09 20:39:31.252086617 +0000 @@ -7,3 +7,848 @@ AS skip_test \gset \if :skip_test \quit +\endif +SET client_encoding TO UTF8; +CREATE SCHEMA collate_tests; +SET search_path = collate_tests; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en-x-icu" NOT NULL +); +\d collate_test1 + Table "collate_tests.collate_test1" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en-x-icu | not null | + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp-x-icu" +); +ERROR: collation "ja_JP.eucjp-x-icu" for encoding "UTF8" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp-x-icu" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo-x-icu" +); +ERROR: collation "foo-x-icu" for encoding "UTF8" does not exist +LINE 3: b text COLLATE "foo-x-icu" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en-x-icu", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en-x-icu", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "collate_tests.collate_test_like" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | | + b | text | en-x-icu | not null | + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv-x-icu" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en-x-icu"; +ERROR: collation mismatch between explicit collations "C" and "en-x-icu" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv-x-icu"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; -- fails +ERROR: collations are not supported by type integer +LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv-x-icu"; + ^ +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en-x-icu" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en-x-icu" > 'äbc' COLLATE "en-x-icu" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv-x-icu" > 'äbc' COLLATE "sv-x-icu" AS "false"; + false +------- + f +(1 row) + +-- upper/lower +CREATE TABLE collate_test10 ( + a int, + x text COLLATE "en-x-icu", + y text COLLATE "tr-x-icu" +); +INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); +SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; + a | lower | lower | upper | upper | initcap | initcap +---+-------+-------+-------+-------+---------+--------- + 1 | hij | hij | HIJ | HİJ | Hij | Hij + 2 | hij | hıj | HIJ | HIJ | Hij | Hıj +(2 rows) + +SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hij +(2 rows) + +SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; + a | x | y +---+-----+----- + 2 | HIJ | HIJ + 1 | hij | hij +(2 rows) + +SELECT lower('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' COLLATE "en-x-icu"); + lower +------------------------------- + abcd 123 #$% ıiii̇ ß ß dždždž σσς +(1 row) + +SELECT casefold('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' COLLATE "en-x-icu"); + casefold +--------------------------------- + abcd 123 #$% ıiii̇ ss ss dždždž σσσ +(1 row) + +SELECT lower('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' COLLATE "tr-x-icu"); + lower +------------------------------- + abcd 123 #$% ıiıi ß ß dždždž σσς +(1 row) + +SELECT casefold('AbCd 123 #$% ıiIİ ẞ ß DŽDždž Σσς' COLLATE "tr-x-icu"); + casefold +--------------------------------- + abcd 123 #$% ıiıi ss ss dždždž σσσ +(1 row) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +SELECT 'Türkiye' COLLATE "en-x-icu" ILIKE '%KI%' AS "true"; + true +------ + t +(1 row) + +SELECT 'Türkiye' COLLATE "tr-x-icu" ILIKE '%KI%' AS "false"; + false +------- + f +(1 row) + +SELECT 'bıt' ILIKE 'BIT' COLLATE "en-x-icu" AS "false"; + false +------- + f +(1 row) + +SELECT 'bıt' ILIKE 'BIT' COLLATE "tr-x-icu" AS "true"; + true +------ + t +(1 row) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- regular expressions +SELECT * FROM collate_test1 WHERE b ~ '^abc$'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ '^abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b ~ 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc$'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* '^abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ~* 'bc'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +CREATE TABLE collate_test6 ( + a int, + b text COLLATE "en-x-icu" +); +INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), + (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), + (9, 'äbç'), (10, 'ÄBÇ'); +SELECT b, + b ~ '^[[:alpha:]]+$' AS is_alpha, + b ~ '^[[:upper:]]+$' AS is_upper, + b ~ '^[[:lower:]]+$' AS is_lower, + b ~ '^[[:digit:]]+$' AS is_digit, + b ~ '^[[:alnum:]]+$' AS is_alnum, + b ~ '^[[:graph:]]+$' AS is_graph, + b ~ '^[[:print:]]+$' AS is_print, + b ~ '^[[:punct:]]+$' AS is_punct, + b ~ '^[[:space:]]+$' AS is_space +FROM collate_test6; + b | is_alpha | is_upper | is_lower | is_digit | is_alnum | is_graph | is_print | is_punct | is_space +-----+----------+----------+----------+----------+----------+----------+----------+----------+---------- + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(10 rows) + +SELECT 'Türkiye' COLLATE "en-x-icu" ~* 'KI' AS "true"; + true +------ + t +(1 row) + +SELECT 'Türkiye' COLLATE "tr-x-icu" ~* 'KI' AS "true"; -- true with ICU + true +------ + t +(1 row) + +SELECT 'bıt' ~* 'BIT' COLLATE "en-x-icu" AS "false"; + false +------- + f +(1 row) + +SELECT 'bıt' ~* 'BIT' COLLATE "tr-x-icu" AS "false"; -- false with ICU + false +------- + f +(1 row) + +-- The following actually exercises the selectivity estimation for ~*. +SELECT relname FROM pg_class WHERE relname ~* '^abc'; + relname +--------- +(0 rows) + +/* not run by default because it requires tr_TR system locale +-- to_char + +SET lc_time TO 'tr_TR'; +SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr-x-icu"); +*/ +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+-------------------------------------------- + collview1 | SELECT a, + + | b + + | FROM collate_test1 + + | WHERE ((b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT a, + + | b + + | FROM collate_test1 + + | ORDER BY (b COLLATE "C"); + collview3 | SELECT a, + + | lower(((x || x) COLLATE "C")) AS lower+ + | FROM collate_test10; +(3 rows) + +-- collation propagation in various expression types +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hıj +(2 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; + a | x | y | lower | lower +---+-----+-----+-------+------- + 1 | hij | hij | hij | hij + 2 | HIJ | HIJ | hij | hıj +(2 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hıj +(2 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hıj +(2 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en-x-icu" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en-x-icu" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en-x-icu" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +CREATE TABLE test_u AS SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- fail +ERROR: no collation was derived for column "b" with collatable type text +HINT: Use the COLLATE clause to set the collation explicitly. +-- ideally this would be a parse-time error, but for now it must be run-time: +select x < y from collate_test10; -- fail +ERROR: could not determine which collation to use for string comparison +HINT: Use the COLLATE clause to set the collation explicitly. +select x || y from collate_test10; -- ok, because || is not collation aware + ?column? +---------- + hijhij + HIJHIJ +(2 rows) + +select x, y from collate_test10 order by x || y; -- not so ok +ERROR: collation mismatch between implicit collations "en-x-icu" and "tr-x-icu" +LINE 1: select x, y from collate_test10 order by x || y; + ^ +HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. +-- collation mismatch between recursive and non-recursive term +WITH RECURSIVE foo(x) AS + (SELECT x FROM (VALUES('a' COLLATE "en-x-icu"),('b')) t(x) + UNION ALL + SELECT (x || 'c') COLLATE "de-x-icu" FROM foo WHERE length(x) < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has collation "en-x-icu" in non-recursive term but collation "de-x-icu" overall +LINE 2: (SELECT x FROM (VALUES('a' COLLATE "en-x-icu"),('b')) t(x... + ^ +HINT: Use the COLLATE clause to set the collation of the non-recursive term. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: syntax error at or near "COLLATE" +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too +CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 $$; +CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql + AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; +SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) +FROM collate_test1 a, collate_test1 b +ORDER BY a.b, b.b; +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/collate.linux.utf8.out /tmp/cirrus-ci-build/src/test/regress/results/collate.linux.utf8.out --- /tmp/cirrus-ci-build/src/test/regress/expected/collate.linux.utf8.out 2026-03-09 20:33:45.132311473 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/collate.linux.utf8.out 2026-03-09 20:39:36.732086068 +0000 @@ -12,17 +12,19 @@ \endif SET client_encoding TO UTF8; CREATE SCHEMA collate_tests; +ERROR: schema "collate_tests" already exists SET search_path = collate_tests; CREATE TABLE collate_test1 ( a int, b text COLLATE "en_US" NOT NULL ); +ERROR: relation "collate_test1" already exists \d collate_test1 Table "collate_tests.collate_test1" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- a | integer | | | - b | text | en_US | not null | + b | text | en-x-icu | not null | CREATE TABLE collate_test_fail ( a int, @@ -48,21 +50,24 @@ CREATE TABLE collate_test_like ( LIKE collate_test1 ); +ERROR: relation "collate_test_like" already exists \d collate_test_like Table "collate_tests.collate_test_like" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- a | integer | | | - b | text | en_US | not null | + b | text | en-x-icu | not null | CREATE TABLE collate_test2 ( a int, b text COLLATE "sv_SE" ); +ERROR: relation "collate_test2" already exists CREATE TABLE collate_test3 ( a int, b text COLLATE "C" ); +ERROR: relation "collate_test3" already exists INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); INSERT INTO collate_test2 SELECT * FROM collate_test1; INSERT INTO collate_test3 SELECT * FROM collate_test1; @@ -70,21 +75,30 @@ a | b ---+----- 3 | bbc -(1 row) + 3 | bbc +(2 rows) SELECT * FROM collate_test2 WHERE b >= 'bbc'; a | b ---+----- 2 | äbc 3 | bbc -(2 rows) + 2 | äbc + 3 | bbc + 2 | äbc + 3 | bbc +(6 rows) SELECT * FROM collate_test3 WHERE b >= 'bbc'; a | b ---+----- 2 | äbc 3 | bbc -(2 rows) + 2 | äbc + 3 | bbc + 2 | äbc + 3 | bbc +(6 rows) SELECT * FROM collate_test3 WHERE b >= 'BBC'; a | b @@ -92,34 +106,47 @@ 1 | abc 2 | äbc 3 | bbc -(3 rows) + 1 | abc + 2 | äbc + 3 | bbc + 1 | abc + 2 | äbc + 3 | bbc +(9 rows) SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; a | b ---+----- 2 | äbc 3 | bbc -(2 rows) + 2 | äbc + 3 | bbc +(4 rows) SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; a | b ---+----- 2 | äbc 3 | bbc -(2 rows) + 2 | äbc + 3 | bbc +(4 rows) SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; a | b ---+----- 2 | äbc 3 | bbc -(2 rows) + 2 | äbc + 3 | bbc +(4 rows) SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; ERROR: collation mismatch between explicit collations "C" and "en_US" LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... ^ CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE"; +ERROR: type "testdomain_sv" already exists CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; -- fails ERROR: collations are not supported by type integer LINE 1: CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE"; @@ -128,93 +155,155 @@ a int, b testdomain_sv ); +ERROR: relation "collate_test4" already exists INSERT INTO collate_test4 SELECT * FROM collate_test1; SELECT a, b FROM collate_test4 ORDER BY b; a | b ---+----- 1 | abc + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC 4 | ABC 3 | bbc + 3 | bbc + 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) CREATE TABLE collate_test5 ( a int, b testdomain_sv COLLATE "en_US" ); +ERROR: relation "collate_test5" already exists INSERT INTO collate_test5 SELECT * FROM collate_test1; SELECT a, b FROM collate_test5 ORDER BY b; a | b ---+----- 1 | abc + 1 | abc + 1 | abc 4 | ABC + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc 2 | äbc 3 | bbc -(4 rows) + 3 | bbc + 3 | bbc +(12 rows) SELECT a, b FROM collate_test1 ORDER BY b; a | b ---+----- 1 | abc + 1 | abc + 4 | ABC 4 | ABC 2 | äbc + 2 | äbc 3 | bbc -(4 rows) + 3 | bbc +(8 rows) SELECT a, b FROM collate_test2 ORDER BY b; a | b ---+----- 1 | abc + 1 | abc + 1 | abc + 4 | ABC 4 | ABC + 4 | ABC + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, b FROM collate_test3 ORDER BY b; a | b ---+----- 4 | ABC + 4 | ABC + 4 | ABC + 1 | abc 1 | abc + 1 | abc + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; a | b ---+----- 4 | ABC + 4 | ABC 1 | abc + 1 | abc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc +(8 rows) -- star expansion SELECT * FROM collate_test1 ORDER BY b; a | b ---+----- 1 | abc + 1 | abc + 4 | ABC 4 | ABC 2 | äbc + 2 | äbc 3 | bbc -(4 rows) + 3 | bbc +(8 rows) SELECT * FROM collate_test2 ORDER BY b; a | b ---+----- 1 | abc + 1 | abc + 1 | abc + 4 | ABC 4 | ABC + 4 | ABC + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT * FROM collate_test3 ORDER BY b; a | b ---+----- 4 | ABC + 4 | ABC + 4 | ABC + 1 | abc + 1 | abc 1 | abc 3 | bbc + 3 | bbc + 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) -- constant expression folding SELECT 'bbc' COLLATE "en_US" > 'äbc' COLLATE "en_US" AS "true"; @@ -235,40 +324,49 @@ x text COLLATE "en_US", y text COLLATE "tr_TR" ); +ERROR: relation "collate_test10" already exists INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; a | lower | lower | upper | upper | initcap | initcap ---+-------+-------+-------+-------+---------+--------- 1 | hij | hij | HIJ | HİJ | Hij | Hij 2 | hij | hıj | HIJ | HIJ | Hij | Hıj -(2 rows) + 1 | hij | hij | HIJ | HİJ | Hij | Hij + 2 | hij | hıj | HIJ | HIJ | Hij | Hıj +(4 rows) SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; a | lower | lower ---+-------+------- 1 | hij | hij 2 | hij | hij -(2 rows) + 1 | hij | hij + 2 | hij | hij +(4 rows) SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; a | x | y ---+-----+----- 2 | HIJ | HIJ + 2 | HIJ | HIJ 1 | hij | hij -(2 rows) + 1 | hij | hij +(4 rows) -- LIKE/ILIKE SELECT * FROM collate_test1 WHERE b LIKE 'abc'; a | b ---+----- 1 | abc -(1 row) + 1 | abc +(2 rows) SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; a | b ---+----- 1 | abc -(1 row) + 1 | abc +(2 rows) SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; a | b @@ -276,21 +374,28 @@ 1 | abc 2 | äbc 3 | bbc -(3 rows) + 1 | abc + 2 | äbc + 3 | bbc +(6 rows) SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; a | b ---+----- 1 | abc 4 | ABC -(2 rows) + 1 | abc + 4 | ABC +(4 rows) SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; a | b ---+----- 1 | abc 4 | ABC -(2 rows) + 1 | abc + 4 | ABC +(4 rows) SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; a | b @@ -299,7 +404,11 @@ 2 | äbc 3 | bbc 4 | ABC -(4 rows) + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) SELECT 'Türkiye' COLLATE "en_US" ILIKE '%KI%' AS "true"; true @@ -336,13 +445,15 @@ a | b ---+----- 1 | abc -(1 row) + 1 | abc +(2 rows) SELECT * FROM collate_test1 WHERE b ~ '^abc'; a | b ---+----- 1 | abc -(1 row) + 1 | abc +(2 rows) SELECT * FROM collate_test1 WHERE b ~ 'bc'; a | b @@ -350,21 +461,28 @@ 1 | abc 2 | äbc 3 | bbc -(3 rows) + 1 | abc + 2 | äbc + 3 | bbc +(6 rows) SELECT * FROM collate_test1 WHERE b ~* '^abc$'; a | b ---+----- 1 | abc 4 | ABC -(2 rows) + 1 | abc + 4 | ABC +(4 rows) SELECT * FROM collate_test1 WHERE b ~* '^abc'; a | b ---+----- 1 | abc 4 | ABC -(2 rows) + 1 | abc + 4 | ABC +(4 rows) SELECT * FROM collate_test1 WHERE b ~* 'bc'; a | b @@ -373,12 +491,17 @@ 2 | äbc 3 | bbc 4 | ABC -(4 rows) + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) CREATE TABLE collate_test6 ( a int, b text COLLATE "en_US" ); +ERROR: relation "collate_test6" already exists INSERT INTO collate_test6 VALUES (1, 'abc'), (2, 'ABC'), (3, '123'), (4, 'ab1'), (5, 'a1!'), (6, 'a c'), (7, '!.;'), (8, ' '), (9, 'äbç'), (10, 'ÄBÇ'); @@ -405,7 +528,17 @@ | f | f | f | f | f | f | t | f | t äbç | t | f | t | f | t | t | t | f | f ÄBÇ | t | t | f | f | t | t | t | f | f -(10 rows) + abc | t | f | t | f | t | t | t | f | f + ABC | t | t | f | f | t | t | t | f | f + 123 | f | f | f | t | t | t | t | f | f + ab1 | f | f | f | f | t | t | t | f | f + a1! | f | f | f | f | f | t | t | f | f + a c | f | f | f | f | f | f | t | f | f + !.; | f | f | f | f | f | t | t | t | f + | f | f | f | f | f | f | t | f | t + äbç | t | f | t | f | t | t | t | f | f + ÄBÇ | t | t | f | f | t | t | t | f | f +(20 rows) SELECT 'Türkiye' COLLATE "en_US" ~* 'KI' AS "true"; true @@ -481,8 +614,11 @@ DETAIL: The given value did not match any of the allowed values for this field. -- backwards parsing CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +ERROR: relation "collview1" already exists CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +ERROR: relation "collview2" already exists CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; +ERROR: relation "collview3" already exists SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'collview%' ORDER BY 1; table_name | view_definition @@ -505,35 +641,57 @@ a | coalesce ---+---------- 1 | abc + 1 | abc + 4 | ABC 4 | ABC 2 | äbc + 2 | äbc 3 | bbc -(4 rows) + 3 | bbc +(8 rows) SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; a | coalesce ---+---------- 1 | abc + 1 | abc + 1 | abc + 4 | ABC 4 | ABC + 4 | ABC + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; a | coalesce ---+---------- 4 | ABC + 4 | ABC + 4 | ABC + 1 | abc 1 | abc + 1 | abc + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; a | lower | lower ---+-------+------- 1 | hij | hij 2 | hij | hıj -(2 rows) + 1 | hij | hij + 2 | hij | hıj +(4 rows) SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; a | b | greatest @@ -542,137 +700,232 @@ 2 | äbc | CCC 3 | bbc | CCC 4 | ABC | CCC -(4 rows) + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(8 rows) SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; a | b | greatest ---+-----+---------- + 4 | ABC | CCC + 1 | abc | CCC + 3 | bbc | CCC + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC 1 | abc | CCC 3 | bbc | CCC 4 | ABC | CCC 2 | äbc | äbc -(4 rows) + 2 | äbc | äbc + 2 | äbc | äbc +(12 rows) SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; a | b | greatest ---+-----+---------- 4 | ABC | CCC + 4 | ABC | CCC + 4 | ABC | CCC + 1 | abc | abc 1 | abc | abc + 1 | abc | abc + 3 | bbc | bbc + 3 | bbc | bbc 3 | bbc | bbc 2 | äbc | äbc -(4 rows) + 2 | äbc | äbc + 2 | äbc | äbc +(12 rows) SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; a | x | y | lower | lower ---+-----+-----+-------+------- 1 | hij | hij | hij | hij 2 | HIJ | HIJ | hij | hıj -(2 rows) + 1 | hij | hij | hij | hij + 2 | HIJ | HIJ | hij | hıj +(4 rows) SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; a | nullif ---+-------- 4 | ABC + 4 | ABC + 2 | äbc 2 | äbc 3 | bbc + 3 | bbc 1 | -(4 rows) + 1 | +(8 rows) SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; a | nullif ---+-------- 4 | ABC + 4 | ABC + 4 | ABC + 3 | bbc 3 | bbc + 3 | bbc + 2 | äbc + 2 | äbc 2 | äbc 1 | -(4 rows) + 1 | + 1 | +(12 rows) SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; a | nullif ---+-------- 4 | ABC + 4 | ABC + 4 | ABC + 3 | bbc 3 | bbc + 3 | bbc + 2 | äbc + 2 | äbc 2 | äbc 1 | -(4 rows) + 1 | + 1 | +(12 rows) SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; a | lower | lower ---+-------+------- 1 | hij | hij 2 | hij | hıj -(2 rows) + 1 | hij | hij + 2 | hij | hıj +(4 rows) SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; a | b ---+------ 4 | ABC + 4 | ABC + 2 | äbc 2 | äbc 1 | abcd + 1 | abcd 3 | bbc -(4 rows) + 3 | bbc +(8 rows) SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; a | b ---+------ 4 | ABC + 4 | ABC + 4 | ABC + 1 | abcd 1 | abcd + 1 | abcd + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; a | b ---+------ 4 | ABC + 4 | ABC + 4 | ABC + 1 | abcd 1 | abcd + 1 | abcd + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) CREATE DOMAIN testdomain AS text; +ERROR: type "testdomain" already exists SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; a | b ---+----- 1 | abc + 1 | abc 4 | ABC + 4 | ABC + 2 | äbc 2 | äbc 3 | bbc -(4 rows) + 3 | bbc +(8 rows) SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; a | b ---+----- 1 | abc + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC 4 | ABC 3 | bbc + 3 | bbc + 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; a | b ---+----- 4 | ABC + 4 | ABC + 4 | ABC 1 | abc + 1 | abc + 1 | abc + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; a | b ---+----- 1 | abc + 1 | abc + 1 | abc + 4 | ABC 4 | ABC + 4 | ABC + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; a | lower | lower ---+-------+------- 1 | hij | hij 2 | hij | hıj -(2 rows) + 1 | hij | hij + 2 | hij | hıj +(4 rows) SELECT min(b), max(b) FROM collate_test1; min | max @@ -693,21 +946,21 @@ (1 row) SELECT array_agg(b ORDER BY b) FROM collate_test1; - array_agg -------------------- - {abc,ABC,äbc,bbc} + array_agg +----------------------------------- + {abc,abc,ABC,ABC,äbc,äbc,bbc,bbc} (1 row) SELECT array_agg(b ORDER BY b) FROM collate_test2; - array_agg -------------------- - {abc,ABC,bbc,äbc} + array_agg +--------------------------------------------------- + {abc,abc,abc,ABC,ABC,ABC,bbc,bbc,bbc,äbc,äbc,äbc} (1 row) SELECT array_agg(b ORDER BY b) FROM collate_test3; - array_agg -------------------- - {ABC,abc,bbc,äbc} + array_agg +--------------------------------------------------- + {ABC,ABC,ABC,abc,abc,abc,bbc,bbc,bbc,äbc,äbc,äbc} (1 row) SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; @@ -715,13 +968,21 @@ ---+----- 1 | abc 1 | abc + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC 4 | ABC 4 | ABC 2 | äbc 2 | äbc + 2 | äbc + 2 | äbc 3 | bbc 3 | bbc -(8 rows) + 3 | bbc + 3 | bbc +(16 rows) SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; a | b @@ -761,10 +1022,22 @@ 2 | äbc 3 | bbc 4 | ABC -(8 rows) + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(20 rows) SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail -ERROR: collation mismatch between implicit collations "en_US" and "C" +ERROR: collation mismatch between implicit collations "en-x-icu" and "C" LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... ^ HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. @@ -778,12 +1051,12 @@ (4 rows) SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail -ERROR: collation mismatch between implicit collations "en_US" and "C" +ERROR: collation mismatch between implicit collations "en-x-icu" and "C" LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... ^ HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail -ERROR: collation mismatch between implicit collations "en_US" and "C" +ERROR: collation mismatch between implicit collations "en-x-icu" and "C" LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... ^ HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. @@ -799,10 +1072,12 @@ ---------- hijhij HIJHIJ -(2 rows) + hijhij + HIJHIJ +(4 rows) select x, y from collate_test10 order by x || y; -- not so ok -ERROR: collation mismatch between implicit collations "en_US" and "tr_TR" +ERROR: collation mismatch between implicit collations "en-x-icu" and "tr-x-icu" LINE 1: select x, y from collate_test10 order by x || y; ^ HINT: You can choose the collation by applying the COLLATE clause to one or both expressions. @@ -825,354 +1100,66 @@ a | b ---+----- 1 | abc + 1 | abc + 4 | ABC 4 | ABC 2 | äbc + 2 | äbc 3 | bbc -(4 rows) + 3 | bbc +(8 rows) SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; a | b ---+----- 1 | abc + 1 | abc + 1 | abc + 4 | ABC 4 | ABC + 4 | ABC + 3 | bbc + 3 | bbc 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; a | b ---+----- 4 | ABC + 4 | ABC + 4 | ABC + 1 | abc + 1 | abc 1 | abc 3 | bbc + 3 | bbc + 3 | bbc 2 | äbc -(4 rows) + 2 | äbc + 2 | äbc +(12 rows) -- propagation of collation in SQL functions (inlined and non-inlined cases) -- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 $$; +ERROR: function "mylt" already exists with same argument types CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 limit 1 $$; +ERROR: function "mylt_noninline" already exists with same argument types CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql AS $$ begin return $1 < $2; end $$; +ERROR: function "mylt_plpgsql" already exists with same argument types SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; - a | b | lt | mylt | mylt_noninline | mylt_plpgsql ------+-----+----+------+----------------+-------------- - abc | abc | f | f | f | f - abc | ABC | t | t | t | t - abc | äbc | t | t | t | t - abc | bbc | t | t | t | t - ABC | abc | f | f | f | f - ABC | ABC | f | f | f | f - ABC | äbc | t | t | t | t - ABC | bbc | t | t | t | t - äbc | abc | f | f | f | f - äbc | ABC | f | f | f | f - äbc | äbc | f | f | f | f - äbc | bbc | t | t | t | t - bbc | abc | f | f | f | f - bbc | ABC | f | f | f | f - bbc | äbc | f | f | f | f - bbc | bbc | f | f | f | f -(16 rows) - -SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, - mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), - mylt_plpgsql(a.b, b.b COLLATE "C") -FROM collate_test1 a, collate_test1 b -ORDER BY a.b, b.b; - a | b | lt | mylt | mylt_noninline | mylt_plpgsql ------+-----+----+------+----------------+-------------- - abc | abc | f | f | f | f - abc | ABC | f | f | f | f - abc | äbc | t | t | t | t - abc | bbc | t | t | t | t - ABC | abc | t | t | t | t - ABC | ABC | f | f | f | f - ABC | äbc | t | t | t | t - ABC | bbc | t | t | t | t - äbc | abc | f | f | f | f - äbc | ABC | f | f | f | f - äbc | äbc | f | f | f | f - äbc | bbc | f | f | f | f - bbc | abc | f | f | f | f - bbc | ABC | f | f | f | f - bbc | äbc | t | t | t | t - bbc | bbc | f | f | f | f -(16 rows) - --- collation override in plpgsql -CREATE FUNCTION mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ -declare - xx text := x; - yy text := y; -begin - return xx < yy; -end -$$; -SELECT mylt2('a', 'B' collate "en_US") as t, mylt2('a', 'B' collate "C") as f; - t | f ----+--- - t | f -(1 row) - -CREATE OR REPLACE FUNCTION - mylt2 (x text, y text) RETURNS boolean LANGUAGE plpgsql AS $$ -declare - xx text COLLATE "POSIX" := x; - yy text := y; -begin - return xx < yy; -end -$$; -SELECT mylt2('a', 'B') as f; - f ---- - f -(1 row) - -SELECT mylt2('a', 'B' collate "C") as fail; -- conflicting collations -ERROR: could not determine which collation to use for string comparison -HINT: Use the COLLATE clause to set the collation explicitly. -CONTEXT: PL/pgSQL function mylt2(text,text) line 6 at RETURN -SELECT mylt2('a', 'B' collate "POSIX") as f; - f ---- - f -(1 row) - --- polymorphism -SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; - unnest --------- - abc - ABC - äbc - bbc -(4 rows) - -SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; - unnest --------- - abc - ABC - bbc - äbc -(4 rows) - -SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; - unnest --------- - ABC - abc - bbc - äbc -(4 rows) - -CREATE FUNCTION dup (anyelement) RETURNS anyelement - AS 'select $1' LANGUAGE sql; -SELECT a, dup(b) FROM collate_test1 ORDER BY 2; - a | dup ----+----- - 1 | abc - 4 | ABC - 2 | äbc - 3 | bbc -(4 rows) - -SELECT a, dup(b) FROM collate_test2 ORDER BY 2; - a | dup ----+----- - 1 | abc - 4 | ABC - 3 | bbc - 2 | äbc -(4 rows) - -SELECT a, dup(b) FROM collate_test3 ORDER BY 2; - a | dup ----+----- - 4 | ABC - 1 | abc - 3 | bbc - 2 | äbc -(4 rows) - --- indexes -CREATE INDEX collate_test1_idx1 ON collate_test1 (b); -CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); -CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically -CREATE INDEX collate_test1_idx4 ON collate_test1 (((b||'foo') COLLATE "POSIX")); -CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE "C"); -- fail -ERROR: collations are not supported by type integer -LINE 1: CREATE INDEX collate_test1_idx5 ON collate_test1 (a COLLATE ... - ^ -CREATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C")); -- fail -ERROR: collations are not supported by type integer -LINE 1: ...ATE INDEX collate_test1_idx6 ON collate_test1 ((a COLLATE "C... - ^ -SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%' ORDER BY 1; - relname | pg_get_indexdef ---------------------+------------------------------------------------------------------------------------------------------------------- - collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_tests.collate_test1 USING btree (b) - collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_tests.collate_test1 USING btree (b COLLATE "C") - collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_tests.collate_test1 USING btree (b COLLATE "C") - collate_test1_idx4 | CREATE INDEX collate_test1_idx4 ON collate_tests.collate_test1 USING btree (((b || 'foo'::text)) COLLATE "POSIX") -(4 rows) - --- schema manipulation commands -CREATE ROLE regress_test_role; -CREATE SCHEMA test_schema; --- We need to do this this way to cope with varying names for encodings: -do $$ -BEGIN - EXECUTE 'CREATE COLLATION test0 (locale = ' || - quote_literal((SELECT datcollate FROM pg_database WHERE datname = current_database())) || ');'; -END -$$; -CREATE COLLATION test0 FROM "C"; -- fail, duplicate name -ERROR: collation "test0" already exists -CREATE COLLATION IF NOT EXISTS test0 FROM "C"; -- ok, skipped -NOTICE: collation "test0" already exists, skipping -CREATE COLLATION IF NOT EXISTS test0 (locale = 'foo'); -- ok, skipped -NOTICE: collation "test0" for encoding "UTF8" already exists, skipping -do $$ -BEGIN - EXECUTE 'CREATE COLLATION test1 (lc_collate = ' || - quote_literal((SELECT datcollate FROM pg_database WHERE datname = current_database())) || - ', lc_ctype = ' || - quote_literal((SELECT datctype FROM pg_database WHERE datname = current_database())) || ');'; -END -$$; -CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail, need lc_ctype -ERROR: parameter "lc_ctype" must be specified -CREATE COLLATION testx (locale = 'nonsense'); -- fail -ERROR: could not create locale "nonsense": No such file or directory -DETAIL: The operating system could not find any locale data for the locale name "nonsense". -CREATE COLLATION test4 FROM nonsense; -ERROR: collation "nonsense" for encoding "UTF8" does not exist -CREATE COLLATION test5 FROM test0; -SELECT collname FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; - collname ----------- - test0 - test1 - test5 -(3 rows) - -ALTER COLLATION test1 RENAME TO test11; -ALTER COLLATION test0 RENAME TO test11; -- fail -ERROR: collation "test11" for encoding "UTF8" already exists in schema "collate_tests" -ALTER COLLATION test1 RENAME TO test22; -- fail -ERROR: collation "test1" for encoding "UTF8" does not exist -ALTER COLLATION test11 OWNER TO regress_test_role; -ALTER COLLATION test11 OWNER TO nonsense; -ERROR: role "nonsense" does not exist -ALTER COLLATION test11 SET SCHEMA test_schema; -COMMENT ON COLLATION test0 IS 'US English'; -SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') - FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) - WHERE collname LIKE 'test%' - ORDER BY 1; - collname | nspname | obj_description -----------+---------------+----------------- - test0 | collate_tests | US English - test11 | test_schema | - test5 | collate_tests | -(3 rows) - -DROP COLLATION test0, test_schema.test11, test5; -DROP COLLATION test0; -- fail -ERROR: collation "test0" for encoding "UTF8" does not exist -DROP COLLATION IF EXISTS test0; -NOTICE: collation "test0" does not exist, skipping -SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; - collname ----------- -(0 rows) - -DROP SCHEMA test_schema; -DROP ROLE regress_test_role; --- ALTER -ALTER COLLATION "en_US" REFRESH VERSION; -NOTICE: version has not changed --- also test for database while we are here -SELECT current_database() AS datname \gset -ALTER DATABASE :"datname" REFRESH COLLATION VERSION; -NOTICE: version has not changed --- dependencies -CREATE COLLATION test0 FROM "C"; -CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); -CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; -CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); -CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; -CREATE TABLE collate_dep_test4t (a int, b text); -CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); -DROP COLLATION test0 RESTRICT; -- fail -ERROR: cannot drop collation test0 because other objects depend on it -DETAIL: column b of table collate_dep_test1 depends on collation test0 -type collate_dep_dom1 depends on collation test0 -column y of composite type collate_dep_test2 depends on collation test0 -view collate_dep_test3 depends on collation test0 -index collate_dep_test4i depends on collation test0 -HINT: Use DROP ... CASCADE to drop the dependent objects too. -DROP COLLATION test0 CASCADE; -NOTICE: drop cascades to 5 other objects -DETAIL: drop cascades to column b of table collate_dep_test1 -drop cascades to type collate_dep_dom1 -drop cascades to column y of composite type collate_dep_test2 -drop cascades to view collate_dep_test3 -drop cascades to index collate_dep_test4i -\d collate_dep_test1 - Table "collate_tests.collate_dep_test1" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - a | integer | | | - -\d collate_dep_test2 - Composite type "collate_tests.collate_dep_test2" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------- - x | integer | | | - -DROP TABLE collate_dep_test1, collate_dep_test4t; -DROP TYPE collate_dep_test2; --- test range types and collations -create type textrange_c as range(subtype=text, collation="C"); -create type textrange_en_us as range(subtype=text, collation="en_US"); -select textrange_c('A','Z') @> 'b'::text; - ?column? ----------- - f -(1 row) - -select textrange_en_us('A','Z') @> 'b'::text; - ?column? ----------- - t -(1 row) - -drop type textrange_c; -drop type textrange_en_us; --- standard collations -SELECT * FROM collate_test2 ORDER BY b COLLATE UCS_BASIC; - a | b ----+----- - 4 | ABC - 1 | abc - 3 | bbc - 2 | äbc -(4 rows) - --- nondeterministic collations --- (not supported with libc provider) -CREATE COLLATION ctest_det (locale = 'en_US.utf8', deterministic = true); -CREATE COLLATION ctest_nondet (locale = 'en_US.utf8', deterministic = false); -ERROR: nondeterministic collations not supported with this provider --- cleanup -SET client_min_messages TO warning; -DROP SCHEMA collate_tests CASCADE; +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/publication.out /tmp/cirrus-ci-build/src/test/regress/results/publication.out --- /tmp/cirrus-ci-build/src/test/regress/expected/publication.out 2026-03-09 20:33:45.204310618 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/publication.out 2026-03-09 20:39:59.612083762 +0000 @@ -1981,225 +1981,8 @@ -- Schema publication that does not include the schema that has the parent table CREATE PUBLICATION pub FOR TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=1); SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+------------+----------+----------- - pub | sch2 | tbl1_part1 | {a} | -(1 row) - -DROP PUBLICATION pub; --- Table publication that does not include the parent table -CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=1); -SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+------------+----------+----------- - pub | sch2 | tbl1_part1 | {a} | -(1 row) - --- Table publication that includes both the parent table and the child table -ALTER PUBLICATION pub ADD TABLE sch1.tbl1; -SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+-----------+----------+----------- - pub | sch1 | tbl1 | {a} | -(1 row) - -DROP PUBLICATION pub; --- Schema publication that does not include the schema that has the parent table -CREATE PUBLICATION pub FOR TABLES IN SCHEMA sch2 WITH (PUBLISH_VIA_PARTITION_ROOT=0); -SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+------------+----------+----------- - pub | sch2 | tbl1_part1 | {a} | -(1 row) - -DROP PUBLICATION pub; --- Table publication that does not include the parent table -CREATE PUBLICATION pub FOR TABLE sch2.tbl1_part1 WITH (PUBLISH_VIA_PARTITION_ROOT=0); -SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+------------+----------+----------- - pub | sch2 | tbl1_part1 | {a} | -(1 row) - --- Table publication that includes both the parent table and the child table -ALTER PUBLICATION pub ADD TABLE sch1.tbl1; -SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+------------+----------+----------- - pub | sch2 | tbl1_part1 | {a} | -(1 row) - -DROP PUBLICATION pub; -DROP TABLE sch2.tbl1_part1; -DROP TABLE sch1.tbl1; -CREATE TABLE sch1.tbl1 (a int) PARTITION BY RANGE(a); -CREATE TABLE sch1.tbl1_part1 PARTITION OF sch1.tbl1 FOR VALUES FROM (1) to (10); -CREATE TABLE sch1.tbl1_part2 PARTITION OF sch1.tbl1 FOR VALUES FROM (10) to (20); -CREATE TABLE sch1.tbl1_part3 (a int) PARTITION BY RANGE(a); -ALTER TABLE sch1.tbl1 ATTACH PARTITION sch1.tbl1_part3 FOR VALUES FROM (20) to (30); -CREATE PUBLICATION pub FOR TABLES IN SCHEMA sch1 WITH (PUBLISH_VIA_PARTITION_ROOT=1); -SELECT * FROM pg_publication_tables; - pubname | schemaname | tablename | attnames | rowfilter ----------+------------+-----------+----------+----------- - pub | sch1 | tbl1 | {a} | -(1 row) - -RESET client_min_messages; -DROP PUBLICATION pub; -DROP TABLE sch1.tbl1; -DROP SCHEMA sch1 cascade; -DROP SCHEMA sch2 cascade; --- ====================================================== --- Test the 'publish_generated_columns' parameter with the following values: --- 'stored', 'none'. -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION pub1 FOR ALL TABLES WITH (publish_generated_columns = stored); -\dRp+ pub1 - Publication pub1 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | t | f | t | t | t | t | stored | f | -(1 row) - -CREATE PUBLICATION pub2 FOR ALL TABLES WITH (publish_generated_columns = none); -\dRp+ pub2 - Publication pub2 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | t | f | t | t | t | t | none | f | -(1 row) - -DROP PUBLICATION pub1; -DROP PUBLICATION pub2; --- Test the 'publish_generated_columns' parameter as 'none' and 'stored' for --- different scenarios with/without generated columns in column lists. -CREATE TABLE gencols (a int, gen1 int GENERATED ALWAYS AS (a * 2) STORED); --- Generated columns in column list, when 'publish_generated_columns'='none' -CREATE PUBLICATION pub1 FOR table gencols(a, gen1) WITH (publish_generated_columns = none); -\dRp+ pub1 - Publication pub1 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | f | f | t | t | t | t | none | f | -Tables: - "public.gencols" (a, gen1) - --- Generated columns in column list, when 'publish_generated_columns'='stored' -CREATE PUBLICATION pub2 FOR table gencols(a, gen1) WITH (publish_generated_columns = stored); -\dRp+ pub2 - Publication pub2 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | f | f | t | t | t | t | stored | f | -Tables: - "public.gencols" (a, gen1) - --- Generated columns in column list, then set 'publish_generated_columns'='none' -ALTER PUBLICATION pub2 SET (publish_generated_columns = none); -\dRp+ pub2 - Publication pub2 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | f | f | t | t | t | t | none | f | -Tables: - "public.gencols" (a, gen1) - --- Remove generated columns from column list, when 'publish_generated_columns'='none' -ALTER PUBLICATION pub2 SET TABLE gencols(a); -\dRp+ pub2 - Publication pub2 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | f | f | t | t | t | t | none | f | -Tables: - "public.gencols" (a) - --- Add generated columns in column list, when 'publish_generated_columns'='none' -ALTER PUBLICATION pub2 SET TABLE gencols(a, gen1); -\dRp+ pub2 - Publication pub2 - Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description ---------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- - regress_publication_user | f | f | t | t | t | t | none | f | -Tables: - "public.gencols" (a, gen1) - -DROP PUBLICATION pub1; -DROP PUBLICATION pub2; -DROP TABLE gencols; -RESET client_min_messages; --- Test that the INSERT ON CONFLICT command correctly checks REPLICA IDENTITY --- when the target table is published. -CREATE TABLE testpub_insert_onconfl_no_ri (a int unique, b int); -CREATE TABLE testpub_insert_onconfl_parted (a int unique, b int) PARTITION by RANGE (a); -CREATE TABLE testpub_insert_onconfl_part_no_ri PARTITION OF testpub_insert_onconfl_parted FOR VALUES FROM (1) TO (10); -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION pub1 FOR ALL TABLES; -RESET client_min_messages; --- fail - missing REPLICA IDENTITY -INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; -ERROR: cannot update table "testpub_insert_onconfl_no_ri" because it does not have a replica identity and publishes updates -HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. --- ok - no updates -INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT DO NOTHING; --- fail - missing REPLICA IDENTITY in partition testpub_insert_onconfl_no_ri -INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; -ERROR: cannot update table "testpub_insert_onconfl_part_no_ri" because it does not have a replica identity and publishes updates -HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. --- ok - no updates -INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING; -DROP PUBLICATION pub1; -DROP TABLE testpub_insert_onconfl_no_ri; -DROP TABLE testpub_insert_onconfl_parted; --- Test that the MERGE command correctly checks REPLICA IDENTITY when the --- target table is published. -CREATE TABLE testpub_merge_no_ri (a int, b int); -CREATE TABLE testpub_merge_pk (a int primary key, b int); -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION pub1 FOR ALL TABLES; -RESET client_min_messages; --- fail - missing REPLICA IDENTITY -MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 - WHEN MATCHED THEN UPDATE SET b = s.b; -ERROR: cannot update table "testpub_merge_no_ri" because it does not have a replica identity and publishes updates -HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. --- fail - missing REPLICA IDENTITY -MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 - WHEN MATCHED THEN DELETE; -ERROR: cannot delete from table "testpub_merge_no_ri" because it does not have a replica identity and publishes deletes -HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE. --- ok - insert and do nothing are not restricted -MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 - WHEN MATCHED THEN DO NOTHING - WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, 0); --- ok - REPLICA IDENTITY is DEFAULT and table has a PK -MERGE INTO testpub_merge_pk USING testpub_merge_no_ri s ON s.a >= 1 - WHEN MATCHED AND s.a > 0 THEN UPDATE SET b = s.b - WHEN MATCHED THEN DELETE; -DROP PUBLICATION pub1; -DROP TABLE testpub_merge_no_ri; -DROP TABLE testpub_merge_pk; -RESET SESSION AUTHORIZATION; -DROP ROLE regress_publication_user, regress_publication_user2; -DROP ROLE regress_publication_user_dummy; --- stage objects for pg_dump tests -CREATE SCHEMA pubme CREATE TABLE t0 (c int, d int) CREATE TABLE t1 (c int); -CREATE SCHEMA pubme2 CREATE TABLE t0 (c int, d int); -SET client_min_messages = 'ERROR'; -CREATE PUBLICATION dump_pub_qual_1ct FOR - TABLE ONLY pubme.t0 (c, d) WHERE (c > 0); -CREATE PUBLICATION dump_pub_qual_2ct FOR - TABLE ONLY pubme.t0 (c) WHERE (c > 0), - TABLE ONLY pubme.t1 (c); -CREATE PUBLICATION dump_pub_nsp_1ct FOR - TABLES IN SCHEMA pubme; -CREATE PUBLICATION dump_pub_nsp_2ct FOR - TABLES IN SCHEMA pubme, - TABLES IN SCHEMA pubme2; -CREATE PUBLICATION dump_pub_all FOR - TABLE ONLY pubme.t0, - TABLE ONLY pubme.t1 WHERE (c < 0), - TABLES IN SCHEMA pubme, - TABLES IN SCHEMA pubme2 - WITH (publish_via_partition_root = true); -RESET client_min_messages; +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/alter_table.out /tmp/cirrus-ci-build/src/test/regress/results/alter_table.out --- /tmp/cirrus-ci-build/src/test/regress/expected/alter_table.out 2026-03-09 20:33:45.128311521 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/alter_table.out 2026-03-09 20:40:24.900081195 +0000 @@ -4211,6 +4211,7 @@ a int, b int ) PARTITION BY RANGE (a, b); +ERROR: relation "range_parted" already exists -- check that violating rows are correctly reported CREATE TABLE part1 ( a int NOT NULL CHECK (a = 1), @@ -4219,10 +4220,11 @@ INSERT INTO part1 VALUES (1, 10); -- Remember the TO bound is exclusive ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); -ERROR: partition constraint of relation "part1" is violated by some row +ERROR: child table "part1" has different type for column "a" -- should be ok after deleting the bad row DELETE FROM part1; ALTER TABLE range_parted ATTACH PARTITION part1 FOR VALUES FROM (1, 1) TO (1, 10); +ERROR: child table "part1" has different type for column "a" -- adding constraints that describe the desired partition constraint -- (or more restrictive) will help skip the validation scan CREATE TABLE part2 ( @@ -4230,6 +4232,7 @@ b int NOT NULL CHECK (b >= 10 AND b < 18) ); ALTER TABLE range_parted ATTACH PARTITION part2 FOR VALUES FROM (1, 10) TO (1, 20); +ERROR: child table "part2" has different type for column "a" -- Create default partition CREATE TABLE partr_def1 PARTITION OF range_parted DEFAULT; -- Only one default partition is allowed, hence, following should give error @@ -4584,6 +4587,9 @@ -- cleanup DROP TABLE list_parted, list_parted2, range_parted, list_parted3; +ERROR: cannot drop desired object(s) because other objects depend on them +DETAIL: view upview depends on table range_parted +HINT: Use DROP ... CASCADE to drop the dependent objects too. DROP TABLE fail_def_part; DROP TABLE hash_parted; -- more tests for certain multi-level partitioning scenarios diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/with.out /tmp/cirrus-ci-build/src/test/regress/results/with.out --- /tmp/cirrus-ci-build/src/test/regress/expected/with.out 2026-03-09 20:33:45.240310189 +0000 +++ /tmp/cirrus-ci-build/src/test/regress/results/with.out 2026-03-09 20:40:13.292082375 +0000 @@ -776,2987 +776,8 @@ where g.f = sg.t ) search depth first by f, t set seq select * from search_graph order by seq; - f | t | label | seq ----+---+------------+------------------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | {"(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} - 2 | 3 | arc 2 -> 3 | {"(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)"} -(7 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union distinct - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+------------------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | {"(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} - 2 | 3 | arc 2 -> 3 | {"(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)"} -(7 rows) - -explain (verbose, costs off) -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq -select * from search_graph order by seq; - QUERY PLAN -------------------------------------------------------------------------------------------------- - Sort - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq - Sort Key: search_graph.seq - CTE search_graph - -> Recursive Union - -> Seq Scan on pg_temp.graph0 g - Output: g.f, g.t, g.label, ROW('0'::bigint, g.f, g.t) - -> Merge Join - Output: g_1.f, g_1.t, g_1.label, ROW(int8inc((sg.seq)."*DEPTH*"), g_1.f, g_1.t) - Merge Cond: (g_1.f = sg.t) - -> Sort - Output: g_1.f, g_1.t, g_1.label - Sort Key: g_1.f - -> Seq Scan on pg_temp.graph0 g_1 - Output: g_1.f, g_1.t, g_1.label - -> Sort - Output: sg.seq, sg.t - Sort Key: sg.t - -> WorkTable Scan on search_graph sg - Output: sg.seq, sg.t - -> CTE Scan on search_graph - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq -(22 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+--------- - 1 | 2 | arc 1 -> 2 | (0,1,2) - 1 | 3 | arc 1 -> 3 | (0,1,3) - 1 | 4 | arc 1 -> 4 | (0,1,4) - 2 | 3 | arc 2 -> 3 | (0,2,3) - 4 | 5 | arc 4 -> 5 | (0,4,5) - 2 | 3 | arc 2 -> 3 | (1,2,3) - 4 | 5 | arc 4 -> 5 | (1,4,5) -(7 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union distinct - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq -select * from search_graph order by seq; - f | t | label | seq ----+---+------------+--------- - 1 | 2 | arc 1 -> 2 | (0,1,2) - 1 | 3 | arc 1 -> 3 | (0,1,3) - 1 | 4 | arc 1 -> 4 | (0,1,4) - 2 | 3 | arc 2 -> 3 | (0,2,3) - 4 | 5 | arc 4 -> 5 | (0,4,5) - 2 | 3 | arc 2 -> 3 | (1,2,3) - 4 | 5 | arc 4 -> 5 | (1,4,5) -(7 rows) - --- a constant initial value causes issues for EXPLAIN -explain (verbose, costs off) -with recursive test as ( - select 1 as x - union all - select x + 1 - from test -) search depth first by x set y -select * from test limit 5; - QUERY PLAN ------------------------------------------------------------------------------------------ - Limit - Output: test.x, test.y - CTE test - -> Recursive Union - -> Result - Output: 1, '{(1)}'::record[] - -> WorkTable Scan on test test_1 - Output: (test_1.x + 1), array_cat(test_1.y, ARRAY[ROW((test_1.x + 1))]) - -> CTE Scan on test - Output: test.x, test.y -(10 rows) - -with recursive test as ( - select 1 as x - union all - select x + 1 - from test -) search depth first by x set y -select * from test limit 5; - x | y ----+----------------------- - 1 | {(1)} - 2 | {(1),(2)} - 3 | {(1),(2),(3)} - 4 | {(1),(2),(3),(4)} - 5 | {(1),(2),(3),(4),(5)} -(5 rows) - -explain (verbose, costs off) -with recursive test as ( - select 1 as x - union all - select x + 1 - from test -) search breadth first by x set y -select * from test limit 5; - QUERY PLAN --------------------------------------------------------------------------------------------- - Limit - Output: test.x, test.y - CTE test - -> Recursive Union - -> Result - Output: 1, '(0,1)'::record - -> WorkTable Scan on test test_1 - Output: (test_1.x + 1), ROW(int8inc((test_1.y)."*DEPTH*"), (test_1.x + 1)) - -> CTE Scan on test - Output: test.x, test.y -(10 rows) - -with recursive test as ( - select 1 as x - union all - select x + 1 - from test -) search breadth first by x set y -select * from test limit 5; - x | y ----+------- - 1 | (0,1) - 2 | (1,2) - 3 | (2,3) - 4 | (3,4) - 5 | (4,5) -(5 rows) - --- various syntax errors -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by foo, tar set seq -select * from search_graph; -ERROR: search column "foo" not in WITH query column list -LINE 7: ) search depth first by foo, tar set seq - ^ -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set label -select * from search_graph; -ERROR: search sequence column name "label" already used in WITH query column list -LINE 7: ) search depth first by f, t set label - ^ -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t, f set seq -select * from search_graph; -ERROR: search column "f" specified more than once -LINE 7: ) search depth first by f, t, f set seq - ^ -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select * from search_graph order by seq; -ERROR: with a SEARCH or CYCLE clause, the left side of the UNION must be a SELECT -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - (select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t) -) search depth first by f, t set seq -select * from search_graph order by seq; -ERROR: with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT --- check that we distinguish same CTE name used at different levels --- (this case could be supported, perhaps, but it isn't today) -with recursive x(col) as ( - select 1 - union - (with x as (select * from x) - select * from x) -) search depth first by col set seq -select * from x; -ERROR: with a SEARCH or CYCLE clause, the recursive reference to WITH query "x" must be at the top level of its right-hand SELECT --- test ruleutils and view expansion -create temp view v_search as -with recursive search_graph(f, t, label) as ( - select * from graph0 g - union all - select g.* - from graph0 g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq -select f, t, label from search_graph; -select pg_get_viewdef('v_search'); - pg_get_viewdef ------------------------------------------------- - WITH RECURSIVE search_graph(f, t, label) AS (+ - SELECT g.f, + - g.t, + - g.label + - FROM graph0 g + - UNION ALL + - SELECT g.f, + - g.t, + - g.label + - FROM graph0 g, + - search_graph sg + - WHERE (g.f = sg.t) + - ) SEARCH DEPTH FIRST BY f, t SET seq + - SELECT f, + - t, + - label + - FROM search_graph; -(1 row) - -select * from v_search; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 -(7 rows) - --- --- test cycle detection --- -create temp table graph( f int, t int, label text ); -insert into graph values - (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1'); -with recursive search_graph(f, t, label, is_cycle, path) as ( - select *, false, array[row(g.f, g.t)] from graph g - union all - select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) - from graph g, search_graph sg - where g.f = sg.t and not is_cycle -) -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- UNION DISTINCT exercises row type hashing support -with recursive search_graph(f, t, label, is_cycle, path) as ( - select *, false, array[row(g.f, g.t)] from graph g - union distinct - select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) - from graph g, search_graph sg - where g.f = sg.t and not is_cycle -) -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- ordering by the path column has same effect as SEARCH DEPTH FIRST -with recursive search_graph(f, t, label, is_cycle, path) as ( - select *, false, array[row(g.f, g.t)] from graph g - union all - select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) - from graph g, search_graph sg - where g.f = sg.t and not is_cycle -) -select * from search_graph order by path; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} -(25 rows) - --- CYCLE clause -explain (verbose, costs off) -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle using path -select * from search_graph; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - CTE Scan on search_graph - Output: search_graph.f, search_graph.t, search_graph.label, search_graph.is_cycle, search_graph.path - CTE search_graph - -> Recursive Union - -> Seq Scan on pg_temp.graph g - Output: g.f, g.t, g.label, false, ARRAY[ROW(g.f, g.t)] - -> Merge Join - Output: g_1.f, g_1.t, g_1.label, CASE WHEN (ROW(g_1.f, g_1.t) = ANY (sg.path)) THEN true ELSE false END, array_cat(sg.path, ARRAY[ROW(g_1.f, g_1.t)]) - Merge Cond: (g_1.f = sg.t) - -> Sort - Output: g_1.f, g_1.t, g_1.label - Sort Key: g_1.f - -> Seq Scan on pg_temp.graph g_1 - Output: g_1.f, g_1.t, g_1.label - -> Sort - Output: sg.path, sg.t - Sort Key: sg.t - -> WorkTable Scan on search_graph sg - Output: sg.path, sg.t - Filter: (NOT sg.is_cycle) -(20 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle using path -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph g - union distinct - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to 'Y' default 'N' using path -select * from search_graph; - f | t | label | is_cycle | path ----+---+------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | N | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | N | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | N | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | N | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | N | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | N | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | N | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | N | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | N | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | N | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | N | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | N | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | N | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | N | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | N | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | N | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | Y | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | N | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | Y | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | Y | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - -explain (verbose, costs off) -with recursive test as ( - select 0 as x - union all - select (x + 1) % 10 - from test -) cycle x set is_cycle using path -select * from test; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - CTE Scan on test - Output: test.x, test.is_cycle, test.path - CTE test - -> Recursive Union - -> Result - Output: 0, false, '{(0)}'::record[] - -> WorkTable Scan on test test_1 - Output: ((test_1.x + 1) % 10), CASE WHEN (ROW(((test_1.x + 1) % 10)) = ANY (test_1.path)) THEN true ELSE false END, array_cat(test_1.path, ARRAY[ROW(((test_1.x + 1) % 10))]) - Filter: (NOT test_1.is_cycle) -(9 rows) - -with recursive test as ( - select 0 as x - union all - select (x + 1) % 10 - from test -) cycle x set is_cycle using path -select * from test; - x | is_cycle | path ----+----------+----------------------------------------------- - 0 | f | {(0)} - 1 | f | {(0),(1)} - 2 | f | {(0),(1),(2)} - 3 | f | {(0),(1),(2),(3)} - 4 | f | {(0),(1),(2),(3),(4)} - 5 | f | {(0),(1),(2),(3),(4),(5)} - 6 | f | {(0),(1),(2),(3),(4),(5),(6)} - 7 | f | {(0),(1),(2),(3),(4),(5),(6),(7)} - 8 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8)} - 9 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)} - 0 | t | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(0)} -(11 rows) - -with recursive test as ( - select 0 as x - union all - select (x + 1) % 10 - from test - where not is_cycle -- redundant, but legal -) cycle x set is_cycle using path -select * from test; - x | is_cycle | path ----+----------+----------------------------------------------- - 0 | f | {(0)} - 1 | f | {(0),(1)} - 2 | f | {(0),(1),(2)} - 3 | f | {(0),(1),(2),(3)} - 4 | f | {(0),(1),(2),(3),(4)} - 5 | f | {(0),(1),(2),(3),(4),(5)} - 6 | f | {(0),(1),(2),(3),(4),(5),(6)} - 7 | f | {(0),(1),(2),(3),(4),(5),(6),(7)} - 8 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8)} - 9 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)} - 0 | t | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(0)} -(11 rows) - --- multiple CTEs -with recursive -graph(f, t, label) as ( - values (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1') -), -search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to true default false using path -select f, t, label from search_graph; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 4 | arc 1 -> 4 - 1 | 3 | arc 1 -> 3 - 1 | 2 | arc 1 -> 2 - 5 | 1 | arc 5 -> 1 - 1 | 4 | arc 1 -> 4 - 1 | 3 | arc 1 -> 3 - 1 | 2 | arc 1 -> 2 - 4 | 5 | arc 4 -> 5 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 1 | 3 | arc 1 -> 3 - 1 | 2 | arc 1 -> 2 - 4 | 5 | arc 4 -> 5 - 2 | 3 | arc 2 -> 3 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 -(25 rows) - --- star expansion -with recursive a as ( - select 1 as b - union all - select * from a -) cycle b set c using p -select * from a; - b | c | p ----+---+----------- - 1 | f | {(1)} - 1 | t | {(1),(1)} -(2 rows) - --- search+cycle -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search depth first by f, t set seq - cycle f, t set is_cycle using path -select * from search_graph; - f | t | label | seq | is_cycle | path ----+---+------------+-------------------------------------------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search breadth first by f, t set seq - cycle f, t set is_cycle using path -select * from search_graph; - f | t | label | seq | is_cycle | path ----+---+------------+---------+----------+------------------------------------------- - 1 | 2 | arc 1 -> 2 | (0,1,2) | f | {"(1,2)"} - 1 | 3 | arc 1 -> 3 | (0,1,3) | f | {"(1,3)"} - 2 | 3 | arc 2 -> 3 | (0,2,3) | f | {"(2,3)"} - 1 | 4 | arc 1 -> 4 | (0,1,4) | f | {"(1,4)"} - 4 | 5 | arc 4 -> 5 | (0,4,5) | f | {"(4,5)"} - 5 | 1 | arc 5 -> 1 | (0,5,1) | f | {"(5,1)"} - 1 | 2 | arc 1 -> 2 | (1,1,2) | f | {"(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | (1,1,3) | f | {"(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | (1,1,4) | f | {"(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | (1,2,3) | f | {"(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | (1,4,5) | f | {"(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | (1,5,1) | f | {"(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | (2,1,2) | f | {"(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | (2,1,3) | f | {"(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | (2,1,4) | f | {"(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | (2,2,3) | f | {"(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | (2,4,5) | f | {"(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | (2,5,1) | f | {"(1,4)","(4,5)","(5,1)"} - 1 | 2 | arc 1 -> 2 | (3,1,2) | f | {"(1,4)","(4,5)","(5,1)","(1,2)"} - 1 | 3 | arc 1 -> 3 | (3,1,3) | f | {"(1,4)","(4,5)","(5,1)","(1,3)"} - 1 | 4 | arc 1 -> 4 | (3,1,4) | t | {"(1,4)","(4,5)","(5,1)","(1,4)"} - 2 | 3 | arc 2 -> 3 | (3,2,3) | f | {"(4,5)","(5,1)","(1,2)","(2,3)"} - 4 | 5 | arc 4 -> 5 | (3,4,5) | t | {"(4,5)","(5,1)","(1,4)","(4,5)"} - 5 | 1 | arc 5 -> 1 | (3,5,1) | t | {"(5,1)","(1,4)","(4,5)","(5,1)"} - 2 | 3 | arc 2 -> 3 | (4,2,3) | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} -(25 rows) - --- various syntax errors -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle foo, tar set is_cycle using path -select * from search_graph; -ERROR: cycle column "foo" not in WITH query column list -LINE 7: ) cycle foo, tar set is_cycle using path - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to true default 55 using path -select * from search_graph; -ERROR: CYCLE types boolean and integer cannot be matched -LINE 7: ) cycle f, t set is_cycle to true default 55 using path - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to point '(1,1)' default point '(0,0)' using path -select * from search_graph; -ERROR: could not identify an equality operator for type point -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set label to true default false using path -select * from search_graph; -ERROR: cycle mark column name "label" already used in WITH query column list -LINE 7: ) cycle f, t set label to true default false using path - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to true default false using label -select * from search_graph; -ERROR: cycle path column name "label" already used in WITH query column list -LINE 7: ) cycle f, t set is_cycle to true default false using label - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set foo to true default false using foo -select * from search_graph; -ERROR: cycle mark column name and cycle path column name are the same -LINE 7: ) cycle f, t set foo to true default false using foo - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t, f set is_cycle to true default false using path -select * from search_graph; -ERROR: cycle column "f" specified more than once -LINE 7: ) cycle f, t, f set is_cycle to true default false using pat... - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search depth first by f, t set foo - cycle f, t set foo to true default false using path -select * from search_graph; -ERROR: search sequence column name and cycle mark column name are the same -LINE 7: ) search depth first by f, t set foo - ^ -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) search depth first by f, t set foo - cycle f, t set is_cycle to true default false using foo -select * from search_graph; -ERROR: search sequence column name and cycle path column name are the same -LINE 7: ) search depth first by f, t set foo - ^ --- test ruleutils and view expansion -create temp view v_cycle1 as -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle using path -select f, t, label from search_graph; -create temp view v_cycle2 as -with recursive search_graph(f, t, label) as ( - select * from graph g - union all - select g.* - from graph g, search_graph sg - where g.f = sg.t -) cycle f, t set is_cycle to 'Y' default 'N' using path -select f, t, label from search_graph; -select pg_get_viewdef('v_cycle1'); - pg_get_viewdef ------------------------------------------------- - WITH RECURSIVE search_graph(f, t, label) AS (+ - SELECT g.f, + - g.t, + - g.label + - FROM graph g + - UNION ALL + - SELECT g.f, + - g.t, + - g.label + - FROM graph g, + - search_graph sg + - WHERE (g.f = sg.t) + - ) CYCLE f, t SET is_cycle USING path + - SELECT f, + - t, + - label + - FROM search_graph; -(1 row) - -select pg_get_viewdef('v_cycle2'); - pg_get_viewdef ------------------------------------------------------------------------------ - WITH RECURSIVE search_graph(f, t, label) AS ( + - SELECT g.f, + - g.t, + - g.label + - FROM graph g + - UNION ALL + - SELECT g.f, + - g.t, + - g.label + - FROM graph g, + - search_graph sg + - WHERE (g.f = sg.t) + - ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+ - SELECT f, + - t, + - label + - FROM search_graph; -(1 row) - -select * from v_cycle1; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 -(25 rows) - -select * from v_cycle2; - f | t | label ----+---+------------ - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 2 | 3 | arc 2 -> 3 - 1 | 4 | arc 1 -> 4 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 1 | 2 | arc 1 -> 2 - 1 | 3 | arc 1 -> 3 - 1 | 4 | arc 1 -> 4 - 2 | 3 | arc 2 -> 3 - 4 | 5 | arc 4 -> 5 - 5 | 1 | arc 5 -> 1 - 2 | 3 | arc 2 -> 3 -(25 rows) - --- --- test multiple WITH queries --- -WITH RECURSIVE - y (id) AS (VALUES (1)), - x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; - id ----- - 1 - 2 - 3 - 4 - 5 -(5 rows) - --- forward reference OK -WITH RECURSIVE - x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS (values (1)) - SELECT * FROM x; - id ----- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - id | id -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | - 7 | - 8 | - 9 | - 10 | -(10 rows) - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - id | id -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | -(6 rows) - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - id ----- - 1 - 2 - 3 - 2 - 3 - 4 - 3 - 4 - 5 - 4 - 5 - 6 - 5 - 6 - 7 - 6 - 7 - 8 - 7 - 8 - 9 - 8 - 9 - 10 - 9 - 10 - 10 -(27 rows) - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - id ----- - 1 - 2 - 3 - 1 - 2 - 3 - 2 - 3 - 4 - 2 - 3 - 4 - 3 - 4 - 5 - 3 - 4 - 5 - 4 - 5 - 6 - 4 - 5 - 6 - 5 - 6 - 7 - 5 - 6 - 7 - 6 - 7 - 8 - 6 - 7 - 8 - 7 - 8 - 9 - 7 - 8 - 9 - 8 - 9 - 10 - 8 - 9 - 10 - 9 - 10 - 9 - 10 - 10 - 10 -(54 rows) - --- --- Test WITH attached to a data-modifying statement --- -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); -WITH t AS ( - SELECT a FROM y -) -INSERT INTO y -SELECT a+20 FROM t RETURNING *; - a ----- - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 -(20 rows) - -WITH t AS ( - SELECT a FROM y -) -UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(20 rows) - -WITH RECURSIVE t(a) AS ( - SELECT 11 - UNION ALL - SELECT a+1 FROM t WHERE a < 50 -) -DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - -DROP TABLE y; --- --- error cases --- -WITH x(n, b) AS (SELECT 1) -SELECT * FROM x; -ERROR: WITH query "x" has 1 columns available but 2 columns specified -LINE 1: WITH x(n, b) AS (SELECT 1) - ^ --- INTERSECT -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x... - ^ -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FR... - ^ --- EXCEPT -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - ^ -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM ... - ^ --- no non-recursive term -WITH RECURSIVE x(n) AS (SELECT n FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x) - ^ --- recursive term in the left hand side (strictly speaking, should allow this) -WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within its non-recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - ^ --- allow this, because we historically have -WITH RECURSIVE x(n) AS ( - WITH x1 AS (SELECT 1 AS n) - SELECT 0 - UNION - SELECT * FROM x1) - SELECT * FROM x; - n ---- - 0 - 1 -(2 rows) - --- but this should be rejected -WITH RECURSIVE x(n) AS ( - WITH x1 AS (SELECT 1 FROM x) - SELECT 0 - UNION - SELECT * FROM x1) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 2: WITH x1 AS (SELECT 1 FROM x) - ^ --- and this too -WITH RECURSIVE x(n) AS ( - (WITH x1 AS (SELECT 1 FROM x) SELECT * FROM x1) - UNION - SELECT 0) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within its non-recursive term -LINE 2: (WITH x1 AS (SELECT 1 FROM x) SELECT * FROM x1) - ^ --- and this -WITH RECURSIVE x(n) AS ( - SELECT 0 UNION SELECT 1 - ORDER BY (SELECT n FROM x)) - SELECT * FROM x; -ERROR: ORDER BY in a recursive query is not implemented -LINE 3: ORDER BY (SELECT n FROM x)) - ^ --- and this -WITH RECURSIVE x(n) AS ( - WITH sub_cte AS (SELECT * FROM x) - DELETE FROM graph RETURNING f) - SELECT * FROM x; -ERROR: recursive query "x" must not contain data-modifying statements -LINE 1: WITH RECURSIVE x(n) AS ( - ^ -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); --- LEFT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) - ^ --- RIGHT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) - ^ --- FULL JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) - ^ --- subquery -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x - WHERE n IN (SELECT * FROM x)) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 2: WHERE n IN (SELECT * FROM x)) - ^ --- aggregate functions -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) - SELECT * FROM x; -ERROR: aggregate functions are not allowed in a recursive query's recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F... - ^ -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) - SELECT * FROM x; -ERROR: aggregate functions are not allowed in a recursive query's recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO... - ^ --- ORDER BY -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - SELECT * FROM x; -ERROR: ORDER BY in a recursive query is not implemented -LINE 1: ...VE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - ^ --- LIMIT/OFFSET -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - SELECT * FROM x; -ERROR: OFFSET in a recursive query is not implemented -LINE 1: ... AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - ^ --- FOR UPDATE -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) - SELECT * FROM x; -ERROR: FOR UPDATE/SHARE in a recursive query is not implemented --- target list has a recursive query name -WITH RECURSIVE x(id) AS (values (1) - UNION ALL - SELECT (SELECT * FROM x) FROM x WHERE id < 5 -) SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 3: SELECT (SELECT * FROM x) FROM x WHERE id < 5 - ^ --- mutual recursive query (not implemented) -WITH RECURSIVE - x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), - y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; -ERROR: mutual recursion between WITH items is not implemented -LINE 2: x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id ... - ^ --- non-linear recursion is not allowed -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - SELECT * FROM - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) AS t -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 7: SELECT i+1 FROM foo WHERE i < 5) AS t - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - EXCEPT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear within EXCEPT -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - INTERSECT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ --- Wrong type induced from non-recursive term -WITH RECURSIVE foo(i) AS - (SELECT i FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has type integer in non-recursive term but type numeric overall -LINE 2: (SELECT i FROM (VALUES(1),(2)) t(i) - ^ -HINT: Cast the output of the non-recursive term to the correct type. --- rejects different typmod, too (should we allow this?) -WITH RECURSIVE foo(i) AS - (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive term but type numeric overall -LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - ^ -HINT: Cast the output of the non-recursive term to the correct type. --- disallow OLD/NEW reference in CTE -CREATE TEMPORARY TABLE x (n integer); -CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD - WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; -ERROR: cannot refer to OLD within WITH query --- --- test for bug #4902 --- -with cte(foo) as ( values(42) ) values((select foo from cte)); - column1 ---------- - 42 -(1 row) - -with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; - foo ------ - 42 -(1 row) - --- test CTE referencing an outer-level variable (to see that changed-parameter --- signaling still works properly after fixing this bug) -select ( with cte(foo) as ( values(f1) ) - select (select foo from cte) ) -from int4_tbl; - foo -------------- - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(5 rows) - -select ( with cte(foo) as ( values(f1) ) - values((select foo from cte)) ) -from int4_tbl; - column1 -------------- - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(5 rows) - --- --- test for bug #19055: interaction of WITH with aggregates --- --- For now, we just throw an error if there's a use of a CTE below the --- semantic level that the SQL standard assigns to the aggregate. --- It's not entirely clear what we could do instead that doesn't risk --- breaking more things than it fixes. -select f1, (with cte1(x,y) as (select 1,2) - select count((select i4.f1 from cte1))) as ss -from int4_tbl i4; -ERROR: outer-level aggregate cannot use a nested CTE -LINE 2: select count((select i4.f1 from cte1))) as ss - ^ -DETAIL: CTE "cte1" is below the aggregate's semantic level. --- --- test for bug #19106: interaction of WITH with aggregates --- --- the initial fix for #19055 was too aggressive and broke this case -explain (verbose, costs off) -with a as ( select id from (values (1), (2)) as v(id) ), - b as ( select max((select sum(id) from a)) as agg ) -select agg from b; - QUERY PLAN --------------------------------------------- - Aggregate - Output: max((InitPlan expr_1).col1) - InitPlan expr_1 - -> Aggregate - Output: sum("*VALUES*".column1) - -> Values Scan on "*VALUES*" - Output: "*VALUES*".column1 - -> Result -(8 rows) - -with a as ( select id from (values (1), (2)) as v(id) ), - b as ( select max((select sum(id) from a)) as agg ) -select agg from b; - agg ------ - 3 -(1 row) - --- --- test for nested-recursive-WITH bug --- -WITH RECURSIVE t(j) AS ( - WITH RECURSIVE s(i) AS ( - VALUES (1) - UNION ALL - SELECT i+1 FROM s WHERE i < 10 - ) - SELECT i FROM s - UNION ALL - SELECT j+1 FROM t WHERE j < 10 -) -SELECT * FROM t; - j ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 5 - 6 - 7 - 8 - 9 - 10 - 6 - 7 - 8 - 9 - 10 - 7 - 8 - 9 - 10 - 8 - 9 - 10 - 9 - 10 - 10 -(55 rows) - --- --- test WITH attached to intermediate-level set operation --- -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM innermost - UNION SELECT 3) -) -SELECT * FROM outermost ORDER BY 1; - x ---- - 1 - 2 - 3 -(3 rows) - -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost -- fail - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; -ERROR: relation "outermost" does not exist -LINE 4: SELECT * FROM outermost -- fail - ^ -DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query. -HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references. -WITH RECURSIVE outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; - x ---- - 1 - 2 -(2 rows) - -WITH RECURSIVE outermost(x) AS ( - WITH innermost as (SELECT 2 FROM outermost) -- fail - SELECT * FROM innermost - UNION SELECT * from outermost -) -SELECT * FROM outermost ORDER BY 1; -ERROR: recursive reference to query "outermost" must not appear within a subquery -LINE 2: WITH innermost as (SELECT 2 FROM outermost) -- fail - ^ --- --- This test will fail with the old implementation of PARAM_EXEC parameter --- assignment, because the "q1" Var passed down to A's targetlist subselect --- looks exactly like the "A.id" Var passed down to C's subselect, causing --- the old code to give them the same runtime PARAM_EXEC slot. But the --- lifespans of the two parameters overlap, thanks to B also reading A. --- -with -A as ( select q2 as id, (select q1) as x from int8_tbl ), -B as ( select id, row_number() over (partition by id) as r from A ), -C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) -select * from C; - id | array --------------------+------------------------------------- - 456 | {456} - 4567890123456789 | {4567890123456789,4567890123456789} - 123 | {123} - 4567890123456789 | {4567890123456789,4567890123456789} - -4567890123456789 | {-4567890123456789} -(5 rows) - --- --- Test CTEs read in non-initialization orders --- -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ALL ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - id_key | row_type | link ---------+----------+------ - 0 | base | 17 - 1 | true | 17 - 2 | true | 17 - 3 | true | 17 - 4 | true | 17 - 5 | true | 17 - 6 | true | 17 -(7 rows) - -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - id_key | row_type | link ---------+----------+------ - 0 | base | 17 - 1 | true | 17 - 2 | true | 17 - 3 | true | 17 - 4 | true | 17 - 5 | true | 17 - 6 | true | 17 -(7 rows) - --- --- Data-modifying statements in WITH --- --- INSERT ... RETURNING -WITH t AS ( - INSERT INTO y - VALUES - (11), - (12), - (13), - (14), - (15), - (16), - (17), - (18), - (19), - (20) - RETURNING * -) -SELECT * FROM t; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(20 rows) - --- UPDATE ... RETURNING -WITH t AS ( - UPDATE y - SET a=a+1 - RETURNING * -) -SELECT * FROM t; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(20 rows) - -SELECT * FROM y; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(20 rows) - --- DELETE ... RETURNING -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -SELECT * FROM t; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(9 rows) - -SELECT * FROM y; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(11 rows) - --- forward reference -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT a+5 FROM t2 WHERE a > 5 - RETURNING * -), t2 AS ( - UPDATE y SET a=a-11 RETURNING * -) -SELECT * FROM t -UNION ALL -SELECT * FROM t2; - a ----- - 11 - 12 - 13 - 14 - 15 - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(16 rows) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 -(16 rows) - --- unconditional DO INSTEAD rule -CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD - INSERT INTO y VALUES(42) RETURNING *; -WITH t AS ( - DELETE FROM y RETURNING * -) -SELECT * FROM t; - a ----- - 42 -(1 row) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 - 42 -(17 rows) - -DROP RULE y_rule ON y; --- check merging of outer CTE with CTE in a rule action -CREATE TEMP TABLE bug6051 AS - select i from generate_series(1,3) as t(i); -SELECT * FROM bug6051; - i ---- - 1 - 2 - 3 -(3 rows) - -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -SELECT * FROM bug6051; - i ---- - 1 - 2 - 3 -(3 rows) - -CREATE TEMP TABLE bug6051_2 (i int); -CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD - INSERT INTO bug6051_2 - VALUES(NEW.i); -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -SELECT * FROM bug6051; - i ---- -(0 rows) - -SELECT * FROM bug6051_2; - i ---- - 1 - 2 - 3 -(3 rows) - --- check INSERT ... SELECT rule actions are disallowed on commands --- that have modifyingCTEs -CREATE OR REPLACE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD - INSERT INTO bug6051_2 - SELECT NEW.i; -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -ERROR: INSERT ... SELECT rule actions are not supported for queries having data-modifying statements in WITH --- silly example to verify that hasModifyingCTE flag is propagated -CREATE TEMP TABLE bug6051_3 AS - SELECT a FROM generate_series(11,13) AS a; -CREATE RULE bug6051_3_ins AS ON INSERT TO bug6051_3 DO INSTEAD - SELECT i FROM bug6051_2; -BEGIN; SET LOCAL debug_parallel_query = on; -WITH t1 AS ( DELETE FROM bug6051_3 RETURNING * ) - INSERT INTO bug6051_3 SELECT * FROM t1; - i ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(9 rows) - -COMMIT; -SELECT * FROM bug6051_3; - a ---- -(0 rows) - --- check that recursive CTE processing doesn't rewrite a CTE more than once --- (must not try to expand GENERATED ALWAYS IDENTITY columns more than once) -CREATE TEMP TABLE id_alw1 (i int GENERATED ALWAYS AS IDENTITY); -CREATE TEMP TABLE id_alw2 (i int GENERATED ALWAYS AS IDENTITY); -CREATE TEMP VIEW id_alw2_view AS SELECT * FROM id_alw2; -CREATE TEMP TABLE id_alw3 (i int GENERATED ALWAYS AS IDENTITY); -CREATE RULE id_alw3_ins AS ON INSERT TO id_alw3 DO INSTEAD - WITH t1 AS (INSERT INTO id_alw1 DEFAULT VALUES RETURNING i) - INSERT INTO id_alw2_view DEFAULT VALUES RETURNING i; -CREATE TEMP VIEW id_alw3_view AS SELECT * FROM id_alw3; -CREATE TEMP TABLE id_alw4 (i int GENERATED ALWAYS AS IDENTITY); -WITH t4 AS (INSERT INTO id_alw4 DEFAULT VALUES RETURNING i) - INSERT INTO id_alw3_view DEFAULT VALUES RETURNING i; - i ---- - 1 -(1 row) - -SELECT * from id_alw1; - i ---- - 1 -(1 row) - -SELECT * from id_alw2; - i ---- - 1 -(1 row) - -SELECT * from id_alw3; - i ---- -(0 rows) - -SELECT * from id_alw4; - i ---- - 1 -(1 row) - --- check case where CTE reference is removed due to optimization -EXPLAIN (VERBOSE, COSTS OFF) -SELECT q1 FROM -( - WITH t_cte AS (SELECT * FROM int8_tbl t) - SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub - FROM int8_tbl i8 -) ss; - QUERY PLAN --------------------------------------- - Subquery Scan on ss - Output: ss.q1 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, NULL::bigint -(4 rows) - -SELECT q1 FROM -( - WITH t_cte AS (SELECT * FROM int8_tbl t) - SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub - FROM int8_tbl i8 -) ss; - q1 ------------------- - 123 - 123 - 4567890123456789 - 4567890123456789 - 4567890123456789 -(5 rows) - -EXPLAIN (VERBOSE, COSTS OFF) -SELECT q1 FROM -( - WITH t_cte AS MATERIALIZED (SELECT * FROM int8_tbl t) - SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub - FROM int8_tbl i8 -) ss; - QUERY PLAN ---------------------------------------------- - Subquery Scan on ss - Output: ss.q1 - -> Seq Scan on public.int8_tbl i8 - Output: i8.q1, NULL::bigint - CTE t_cte - -> Seq Scan on public.int8_tbl t - Output: t.q1, t.q2 -(7 rows) - -SELECT q1 FROM -( - WITH t_cte AS MATERIALIZED (SELECT * FROM int8_tbl t) - SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub - FROM int8_tbl i8 -) ss; - q1 ------------------- - 123 - 123 - 4567890123456789 - 4567890123456789 - 4567890123456789 -(5 rows) - --- a truly recursive CTE in the same list -WITH RECURSIVE t(a) AS ( - SELECT 0 - UNION ALL - SELECT a+1 FROM t WHERE a+1 < 5 -), t2 as ( - INSERT INTO y - SELECT * FROM t RETURNING * -) -SELECT * FROM t2 JOIN y USING (a) ORDER BY a; - a ---- - 0 - 1 - 2 - 3 - 4 -(5 rows) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 - 42 - 0 - 1 - 2 - 3 - 4 -(22 rows) - --- data-modifying WITH in a modifying statement -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -INSERT INTO y SELECT -a FROM t RETURNING *; - a ------ - 0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - 0 - -1 - -2 - -3 - -4 -(16 rows) - -SELECT * FROM y; - a ------ - 11 - 12 - 13 - 14 - 15 - 42 - 0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - 0 - -1 - -2 - -3 - -4 -(22 rows) - --- check that WITH query is run to completion even if outer query isn't -WITH t AS ( - UPDATE y SET a = a * 100 RETURNING * -) -SELECT * FROM t LIMIT 10; - a ------- - 1100 - 1200 - 1300 - 1400 - 1500 - 4200 - 0 - -100 - -200 - -300 -(10 rows) - -SELECT * FROM y; - a -------- - 1100 - 1200 - 1300 - 1400 - 1500 - 4200 - 0 - -100 - -200 - -300 - -400 - -500 - -600 - -700 - -800 - -900 - -1000 - 0 - -100 - -200 - -300 - -400 -(22 rows) - --- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE -CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; -ALTER TABLE withz ADD UNIQUE (k); -WITH t AS ( - INSERT INTO withz SELECT i, 'insert' - FROM generate_series(0, 16) i - ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' - RETURNING * -) -SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; - k | v | a ----+--------+--- - 0 | insert | 0 - 0 | insert | 0 -(2 rows) - --- Test EXCLUDED.* reference within CTE -WITH aa AS ( - INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v - WHERE withz.k != EXCLUDED.k - RETURNING * -) -SELECT * FROM aa; - k | v ----+--- -(0 rows) - --- New query/snapshot demonstrates side-effects of previous query. -SELECT * FROM withz ORDER BY k; - k | v -----+------------------ - 0 | insert - 1 | 1 v, now update - 2 | insert - 3 | insert - 4 | 4 v, now update - 5 | insert - 6 | insert - 7 | 7 v, now update - 8 | insert - 9 | insert - 10 | 10 v, now update - 11 | insert - 12 | insert - 13 | 13 v, now update - 14 | insert - 15 | insert - 16 | 16 v, now update -(17 rows) - --- --- Ensure subqueries within the update clause work, even if they --- reference outside values --- -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); --- Update a row more than once, in different parts of a wCTE. That is --- an allowed, presumably very rare, edge case, but since it was --- broken in the past, having a test seems worthwhile. -WITH simpletup AS ( - SELECT 2 k, 'Green' v), -upsert_cte AS ( - INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO - UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) - RETURNING k, v) -INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO -UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) -RETURNING k, v; - k | v ----+--- -(0 rows) - -DROP TABLE withz; --- WITH referenced by MERGE statement -CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; -ALTER TABLE m ADD UNIQUE (k); -WITH RECURSIVE cte_basic AS (SELECT 1 a, 'cte_basic val' b) -MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k -WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1) -WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); -ERROR: WITH RECURSIVE is not supported for MERGE statement --- Basic: -WITH cte_basic AS MATERIALIZED (SELECT 1 a, 'cte_basic val' b) -MERGE INTO m USING (select 0 k, 'merge source SubPlan' v offset 0) o ON m.k=o.k -WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1) -WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); --- Examine -SELECT * FROM m where k = 0; - k | v ----+---------------------- - 0 | merge source SubPlan -(1 row) - --- See EXPLAIN output for same query: -EXPLAIN (VERBOSE, COSTS OFF) -WITH cte_basic AS MATERIALIZED (SELECT 1 a, 'cte_basic val' b) -MERGE INTO m USING (select 0 k, 'merge source SubPlan' v offset 0) o ON m.k=o.k -WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1) -WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); - QUERY PLAN -------------------------------------------------------------------- - Merge on public.m - CTE cte_basic - -> Result - Output: 1, 'cte_basic val'::text - -> Hash Right Join - Output: m.ctid, o.k, o.v, o.* - Hash Cond: (m.k = o.k) - -> Seq Scan on public.m - Output: m.ctid, m.k - -> Hash - Output: o.k, o.v, o.* - -> Subquery Scan on o - Output: o.k, o.v, o.* - -> Result - Output: 0, 'merge source SubPlan'::text - SubPlan expr_1 - -> Limit - Output: ((cte_basic.b || ' merge update'::text)) - -> CTE Scan on cte_basic - Output: (cte_basic.b || ' merge update'::text) - Filter: (cte_basic.a = m.k) -(21 rows) - --- InitPlan -WITH cte_init AS MATERIALIZED (SELECT 1 a, 'cte_init val' b) -MERGE INTO m USING (select 1 k, 'merge source InitPlan' v offset 0) o ON m.k=o.k -WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1) -WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); --- Examine -SELECT * FROM m where k = 1; - k | v ----+--------------------------- - 1 | cte_init val merge update -(1 row) - --- See EXPLAIN output for same query: -EXPLAIN (VERBOSE, COSTS OFF) -WITH cte_init AS MATERIALIZED (SELECT 1 a, 'cte_init val' b) -MERGE INTO m USING (select 1 k, 'merge source InitPlan' v offset 0) o ON m.k=o.k -WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1) -WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v); - QUERY PLAN --------------------------------------------------------------------- - Merge on public.m - CTE cte_init - -> Result - Output: 1, 'cte_init val'::text - InitPlan expr_1 - -> Limit - Output: ((cte_init.b || ' merge update'::text)) - -> CTE Scan on cte_init - Output: (cte_init.b || ' merge update'::text) - Filter: (cte_init.a = 1) - -> Hash Right Join - Output: m.ctid, o.k, o.v, o.* - Hash Cond: (m.k = o.k) - -> Seq Scan on public.m - Output: m.ctid, m.k - -> Hash - Output: o.k, o.v, o.* - -> Subquery Scan on o - Output: o.k, o.v, o.* - -> Result - Output: 1, 'merge source InitPlan'::text -(21 rows) - --- MERGE source comes from CTE: -WITH merge_source_cte AS MATERIALIZED (SELECT 15 a, 'merge_source_cte val' b) -MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a -WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15) -WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte)); --- Examine -SELECT * FROM m where k = 15; - k | v -----+-------------------------------------------------------------- - 15 | merge_source_cte val(15,"merge_source_cte val") merge insert -(1 row) - --- See EXPLAIN output for same query: -EXPLAIN (VERBOSE, COSTS OFF) -WITH merge_source_cte AS MATERIALIZED (SELECT 15 a, 'merge_source_cte val' b) -MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a -WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15) -WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte)); - QUERY PLAN ------------------------------------------------------------------------------------------------------ - Merge on public.m - CTE merge_source_cte - -> Result - Output: 15, 'merge_source_cte val'::text - InitPlan expr_1 - -> CTE Scan on merge_source_cte merge_source_cte_1 - Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text) - Filter: (merge_source_cte_1.a = 15) - InitPlan expr_2 - -> CTE Scan on merge_source_cte merge_source_cte_2 - Output: ((merge_source_cte_2.*)::text || ' merge insert'::text) - -> Hash Right Join - Output: m.ctid, merge_source_cte.a, merge_source_cte.b, merge_source_cte.* - Hash Cond: (m.k = merge_source_cte.a) - -> Seq Scan on public.m - Output: m.ctid, m.k - -> Hash - Output: merge_source_cte.a, merge_source_cte.b, merge_source_cte.* - -> CTE Scan on merge_source_cte - Output: merge_source_cte.a, merge_source_cte.b, merge_source_cte.* -(20 rows) - -DROP TABLE m; --- check that run to completion happens in proper ordering -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 3); -CREATE TEMPORARY TABLE yy (a INTEGER); -WITH RECURSIVE t1 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -), t2 AS ( - INSERT INTO yy SELECT * FROM t1 RETURNING * -) -SELECT 1; - ?column? ----------- - 1 -(1 row) - -SELECT * FROM y; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 -(6 rows) - -SELECT * FROM yy; - a ---- - 1 - 2 - 3 -(3 rows) - -WITH RECURSIVE t1 AS ( - INSERT INTO yy SELECT * FROM t2 RETURNING * -), t2 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -) -SELECT 1; - ?column? ----------- - 1 -(1 row) - -SELECT * FROM y; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(12 rows) - -SELECT * FROM yy; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(9 rows) - --- triggers -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 10); -CREATE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger: a = %', new.a; - return new; -end; -$$ LANGUAGE plpgsql; -CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (21), - (22), - (23) - RETURNING * -) -SELECT * FROM t; -NOTICE: y_trigger: a = 21 -NOTICE: y_trigger: a = 22 -NOTICE: y_trigger: a = 23 - a ----- - 21 - 22 - 23 -(3 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 -(13 rows) - -DROP TRIGGER y_trig ON y; -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (31), - (32), - (33) - RETURNING * -) -SELECT * FROM t LIMIT 1; -NOTICE: y_trigger: a = 31 -NOTICE: y_trigger: a = 32 -NOTICE: y_trigger: a = 33 - a ----- - 31 -(1 row) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 31 - 32 - 33 -(16 rows) - -DROP TRIGGER y_trig ON y; -CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger'; - return null; -end; -$$ LANGUAGE plpgsql; -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (41), - (42), - (43) - RETURNING * -) -SELECT * FROM t; -NOTICE: y_trigger - a ----- - 41 - 42 - 43 -(3 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 31 - 32 - 33 - 41 - 42 - 43 -(19 rows) - -DROP TRIGGER y_trig ON y; -DROP FUNCTION y_trigger(); --- WITH attached to inherited UPDATE or DELETE -CREATE TEMP TABLE parent ( id int, val text ); -CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); -CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); -INSERT INTO parent VALUES ( 1, 'p1' ); -INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); -INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); -WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) -UPDATE parent SET id = id + totalid FROM rcte; -SELECT * FROM parent; - id | val -----+----- - 72 | p1 - 82 | c11 - 83 | c12 - 94 | c21 - 95 | c22 -(5 rows) - -WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) -UPDATE parent SET id = id + newid FROM wcte; -SELECT * FROM parent; - id | val ------+----- - 114 | p1 - 42 | new - 124 | c11 - 125 | c12 - 136 | c21 - 137 | c22 -(6 rows) - -WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) -DELETE FROM parent USING rcte WHERE id = maxid; -SELECT * FROM parent; - id | val ------+----- - 114 | p1 - 42 | new - 124 | c11 - 125 | c12 - 136 | c21 -(5 rows) - -WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) -DELETE FROM parent USING wcte WHERE id = newid; -SELECT * FROM parent; - id | val ------+------ - 114 | p1 - 124 | c11 - 125 | c12 - 136 | c21 - 42 | new2 -(5 rows) - --- check EXPLAIN VERBOSE for a wCTE with RETURNING -EXPLAIN (VERBOSE, COSTS OFF) -WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) -DELETE FROM a_star USING wcte WHERE aa = q2; - QUERY PLAN ---------------------------------------------------------------------------- - Delete on public.a_star - Delete on public.a_star a_star_1 - Delete on public.b_star a_star_2 - Delete on public.c_star a_star_3 - Delete on public.d_star a_star_4 - Delete on public.e_star a_star_5 - Delete on public.f_star a_star_6 - CTE wcte - -> Insert on public.int8_tbl - Output: int8_tbl.q2 - -> Result - Output: '42'::bigint, '47'::bigint - -> Hash Join - Output: wcte.*, a_star.tableoid, a_star.ctid - Hash Cond: (a_star.aa = wcte.q2) - -> Append - -> Seq Scan on public.a_star a_star_1 - Output: a_star_1.aa, a_star_1.tableoid, a_star_1.ctid - -> Seq Scan on public.b_star a_star_2 - Output: a_star_2.aa, a_star_2.tableoid, a_star_2.ctid - -> Seq Scan on public.c_star a_star_3 - Output: a_star_3.aa, a_star_3.tableoid, a_star_3.ctid - -> Seq Scan on public.d_star a_star_4 - Output: a_star_4.aa, a_star_4.tableoid, a_star_4.ctid - -> Seq Scan on public.e_star a_star_5 - Output: a_star_5.aa, a_star_5.tableoid, a_star_5.ctid - -> Seq Scan on public.f_star a_star_6 - Output: a_star_6.aa, a_star_6.tableoid, a_star_6.ctid - -> Hash - Output: wcte.*, wcte.q2 - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 -(32 rows) - --- error cases --- data-modifying WITH tries to use its own output -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT * FROM t -) -VALUES(FALSE); -ERROR: recursive query "t" must not contain data-modifying statements -LINE 1: WITH RECURSIVE t AS ( - ^ --- no RETURNING in a referenced data-modifying WITH -WITH t AS ( - INSERT INTO y VALUES(0) -) -SELECT * FROM t; -ERROR: WITH query "t" does not have a RETURNING clause -LINE 4: SELECT * FROM t; - ^ --- RETURNING tries to return its own output -WITH RECURSIVE t(action, a) AS ( - MERGE INTO y USING (VALUES (11)) v(a) ON y.a = v.a - WHEN NOT MATCHED THEN INSERT VALUES (v.a) - RETURNING merge_action(), (SELECT a FROM t) -) -SELECT * FROM t; -ERROR: recursive query "t" must not contain data-modifying statements -LINE 1: WITH RECURSIVE t(action, a) AS ( - ^ --- data-modifying WITH allowed only at the top level -SELECT * FROM ( - WITH t AS (UPDATE y SET a=a+1 RETURNING *) - SELECT * FROM t -) ss; -ERROR: WITH clause containing a data-modifying statement must be at the top level -LINE 2: WITH t AS (UPDATE y SET a=a+1 RETURNING *) - ^ --- most variants of rules aren't allowed -CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: conditional DO INSTEAD rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTHING; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: DO INSTEAD NOTHING rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTIFY foo; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO ALSO NOTIFY foo; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: DO ALSO rules are not supported for data-modifying statements in WITH -CREATE OR REPLACE RULE y_rule AS ON INSERT TO y - DO INSTEAD (NOTIFY foo; NOTIFY bar); -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: multi-statement DO INSTEAD rules are not supported for data-modifying statements in WITH -DROP RULE y_rule ON y; --- check that parser lookahead for WITH doesn't cause any odd behavior -create table foo (with baz); -- fail, WITH is a reserved word -ERROR: syntax error at or near "with" -LINE 1: create table foo (with baz); - ^ -create table foo (with ordinality); -- fail, WITH is a reserved word -ERROR: syntax error at or near "with" -LINE 1: create table foo (with ordinality); - ^ -with ordinality as (select 1 as x) select * from ordinality; - x ---- - 1 -(1 row) - --- check sane response to attempt to modify CTE relation -WITH with_test AS (SELECT 42) INSERT INTO with_test VALUES (1); -ERROR: relation "with_test" does not exist -LINE 1: WITH with_test AS (SELECT 42) INSERT INTO with_test VALUES (... - ^ --- check response to attempt to modify table with same name as a CTE (perhaps --- surprisingly it works, because CTEs don't hide tables from data-modifying --- statements) -create temp table with_test (i int); -with with_test as (select 42) insert into with_test select * from with_test; -select * from with_test; - i ----- - 42 -(1 row) - -drop table with_test; +FATAL: fatal llvm error: Broken module found, compilation aborted! +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost