Commit 45639a05 authored by Tom Lane's avatar Tom Lane

Avoid invalidating all foreign-join cached plans when user mappings change.

We must not push down a foreign join when the foreign tables involved
should be accessed under different user mappings.  Previously we tried
to enforce that rule literally during planning, but that meant that the
resulting plans were dependent on the current contents of the
pg_user_mapping catalog, and we had to blow away all cached plans
containing any remote join when anything at all changed in pg_user_mapping.
This could have been improved somewhat, but the fact that a syscache inval
callback has very limited info about what changed made it hard to do better
within that design.  Instead, let's change the planner to not consider user
mappings per se, but to allow a foreign join if both RTEs have the same
checkAsUser value.  If they do, then they necessarily will use the same
user mapping at runtime, and we don't need to know specifically which one
that is.  Post-plan-time changes in pg_user_mapping no longer require any
plan invalidation.

This rule does give up some optimization ability, to wit where two foreign
table references come from views with different owners or one's from a view
and one's directly in the query, but nonetheless the same user mapping
would have applied.  We'll sacrifice the first case, but to not regress
more than we have to in the second case, allow a foreign join involving
both zero and nonzero checkAsUser values if the nonzero one is the same as
the prevailing effective userID.  In that case, mark the plan as only
runnable by that userID.

The plancache code already had a notion of plans being userID-specific,
in order to support RLS.  It was a little confused though, in particular
lacking clarity of thought as to whether it was the rewritten query or just
the finished plan that's dependent on the userID.  Rearrange that code so
that it's clearer what depends on which, and so that the same logic applies
to both RLS-injected role dependency and foreign-join-injected role
dependency.

Note that this patch doesn't remove the other issue mentioned in the
original complaint, which is that while we'll reliably stop using a foreign
join if it's disallowed in a new context, we might fail to start using a
foreign join if it's now allowed, but we previously created a generic
cached plan that didn't use one.  It was agreed that the chance of winning
that way was not high enough to justify the much larger number of plan
invalidations that would have to occur if we tried to cause it to happen.

In passing, clean up randomly-varying spelling of EXPLAIN commands in
postgres_fdw.sql, and fix a COSTS ON example that had been allowed to
leak into the committed tests.

This reverts most of commits fbe5a3fb and 5d4171d1, which were the
previous attempt at ensuring we wouldn't push down foreign joins that
span permissions contexts.

Etsuro Fujita and Tom Lane

Discussion: <d49c1e5b-f059-20f4-c132-e9752ee0113e@lab.ntt.co.jp>
parent 533e9c6b
...@@ -192,7 +192,7 @@ ALTER FOREIGN TABLE ft2 OPTIONS (use_remote_estimate 'true'); ...@@ -192,7 +192,7 @@ ALTER FOREIGN TABLE ft2 OPTIONS (use_remote_estimate 'true');
-- simple queries -- simple queries
-- =================================================================== -- ===================================================================
-- single table without alias -- single table without alias
EXPLAIN (COSTS false) SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10; EXPLAIN (COSTS OFF) SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------- ---------------------------
Limit Limit
...@@ -215,7 +215,7 @@ SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10; ...@@ -215,7 +215,7 @@ SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10;
(10 rows) (10 rows)
-- single table with alias - also test that tableoid sort is not pushed to remote side -- single table with alias - also test that tableoid sort is not pushed to remote side
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------
Limit Limit
...@@ -244,7 +244,7 @@ SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10; ...@@ -244,7 +244,7 @@ SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
(10 rows) (10 rows)
-- whole-row reference -- whole-row reference
EXPLAIN (VERBOSE, COSTS false) SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------
Limit Limit
...@@ -276,7 +276,7 @@ SELECT * FROM ft1 WHERE false; ...@@ -276,7 +276,7 @@ SELECT * FROM ft1 WHERE false;
(0 rows) (0 rows)
-- with WHERE clause -- with WHERE clause
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1'; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -291,7 +291,7 @@ SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1'; ...@@ -291,7 +291,7 @@ SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
(1 row) (1 row)
-- with FOR UPDATE/SHARE -- with FOR UPDATE/SHARE
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE;
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------
LockRows LockRows
...@@ -307,7 +307,7 @@ SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE; ...@@ -307,7 +307,7 @@ SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE;
101 | 1 | 00101 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo 101 | 1 | 00101 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------
LockRows LockRows
...@@ -380,7 +380,7 @@ SELECT 'fixed', NULL FROM ft1 t1 WHERE c1 = 1; ...@@ -380,7 +380,7 @@ SELECT 'fixed', NULL FROM ft1 t1 WHERE c1 = 1;
SET enable_hashjoin TO false; SET enable_hashjoin TO false;
SET enable_nestloop TO false; SET enable_nestloop TO false;
-- inner join; expressions in the clauses appear in the equivalence class list -- inner join; expressions in the clauses appear in the equivalence class list
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------
...@@ -413,7 +413,7 @@ SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFF ...@@ -413,7 +413,7 @@ SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFF
-- outer join; expressions in the clauses do not appear in equivalence class -- outer join; expressions in the clauses do not appear in equivalence class
-- list but no output change as compared to the previous query -- list but no output change as compared to the previous query
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------
...@@ -446,7 +446,7 @@ SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1" ...@@ -446,7 +446,7 @@ SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1"
-- A join between local table and foreign join. ORDER BY clause is added to the -- A join between local table and foreign join. ORDER BY clause is added to the
-- foreign join so that the local table can be joined using merge join strategy. -- foreign join so that the local table can be joined using merge join strategy.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -481,7 +481,7 @@ SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3. ...@@ -481,7 +481,7 @@ SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.
-- Test similar to above, except that the full join prevents any equivalence -- Test similar to above, except that the full join prevents any equivalence
-- classes from being merged. This produces single relation equivalence classes -- classes from being merged. This produces single relation equivalence classes
-- included in join restrictions. -- included in join restrictions.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -514,7 +514,7 @@ SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 ...@@ -514,7 +514,7 @@ SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2
(10 rows) (10 rows)
-- Test similar to above with all full outer joins -- Test similar to above with all full outer joins
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -551,7 +551,7 @@ RESET enable_nestloop; ...@@ -551,7 +551,7 @@ RESET enable_nestloop;
-- =================================================================== -- ===================================================================
-- WHERE with remotely-executable conditions -- WHERE with remotely-executable conditions
-- =================================================================== -- ===================================================================
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- Var, OpExpr(b), Const EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- Var, OpExpr(b), Const
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -559,7 +559,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- ...@@ -559,7 +559,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; --
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 = 0; -- BoolExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 = 0; -- BoolExpr
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -567,7 +567,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 ...@@ -567,7 +567,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 100)) AND ((c2 = 0)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 100)) AND ((c2 = 0))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- NullTest EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- NullTest
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -575,7 +575,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- ...@@ -575,7 +575,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NULL; --
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" IS NULL)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" IS NULL))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -583,7 +583,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- ...@@ -583,7 +583,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; --
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" IS NOT NULL)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" IS NOT NULL))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -591,7 +591,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; ...@@ -591,7 +591,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1;
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((round(abs("C 1"), 0) = 1::numeric)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((round(abs("C 1"), 0) = 1::numeric))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l)
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -599,7 +599,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- ...@@ -599,7 +599,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = -c1; --
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = (- "C 1"))) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = (- "C 1")))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r)
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -607,7 +607,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- ...@@ -607,7 +607,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE 1 = c1!; --
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((1::numeric = ("C 1" !))) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((1::numeric = ("C 1" !)))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -615,7 +615,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DI ...@@ -615,7 +615,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DI
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((("C 1" IS NOT NULL) IS DISTINCT FROM ("C 1" IS NOT NULL))) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((("C 1" IS NOT NULL) IS DISTINCT FROM ("C 1" IS NOT NULL)))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -623,7 +623,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, ...@@ -623,7 +623,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1,
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = ANY (ARRAY[c2, 1, ("C 1" + 0)]))) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = ANY (ARRAY[c2, 1, ("C 1" + 0)])))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -631,7 +631,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[ ...@@ -631,7 +631,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = ((ARRAY["C 1", c2, 3])[1]))) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = ((ARRAY["C 1", c2, 3])[1])))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -639,7 +639,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; ...@@ -639,7 +639,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar';
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((c6 = E'foo''s\\bar'::text)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((c6 = E'foo''s\\bar'::text))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------- -------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -649,7 +649,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't ...@@ -649,7 +649,7 @@ EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't
(4 rows) (4 rows)
-- parameterized remote path for foreign table -- parameterized remote path for foreign table
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM "S 1"."T 1" a, ft2 b WHERE a."C 1" = 47 AND b.c1 = a.c2; SELECT * FROM "S 1"."T 1" a, ft2 b WHERE a."C 1" = 47 AND b.c1 = a.c2;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
...@@ -670,7 +670,7 @@ SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2; ...@@ -670,7 +670,7 @@ SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
(1 row) (1 row)
-- check both safe and unsafe join conditions -- check both safe and unsafe join conditions
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft2 a, ft2 b SELECT * FROM ft2 a, ft2 b
WHERE a.c2 = 6 AND b.c1 = a.c1 AND a.c8 = 'foo' AND b.c7 = upper(a.c7); WHERE a.c2 = 6 AND b.c1 = a.c1 AND a.c8 = 'foo' AND b.c7 = upper(a.c7);
QUERY PLAN QUERY PLAN
...@@ -814,7 +814,7 @@ SELECT * FROM ft2 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft1 WHERE c1 < 5)); ...@@ -814,7 +814,7 @@ SELECT * FROM ft2 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft1 WHERE c1 < 5));
-- we should not push order by clause with volatile expressions or unsafe -- we should not push order by clause with volatile expressions or unsafe
-- collations -- collations
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft2 ORDER BY ft2.c1, random(); SELECT * FROM ft2 ORDER BY ft2.c1, random();
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -826,7 +826,7 @@ EXPLAIN (VERBOSE, COSTS false) ...@@ -826,7 +826,7 @@ EXPLAIN (VERBOSE, COSTS false)
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1"
(6 rows) (6 rows)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft2 ORDER BY ft2.c1, ft2.c3 collate "C"; SELECT * FROM ft2 ORDER BY ft2.c1, ft2.c3 collate "C";
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -851,7 +851,7 @@ CREATE OPERATOR === ( ...@@ -851,7 +851,7 @@ CREATE OPERATOR === (
COMMUTATOR = === COMMUTATOR = ===
); );
-- built-in operators and functions can be shipped for remote execution -- built-in operators and functions can be shipped for remote execution
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2);
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------- --------------------------------------------------------------------------
...@@ -868,7 +868,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); ...@@ -868,7 +868,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2);
9 9
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------- ---------------------------------------------------------------------
...@@ -886,7 +886,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2; ...@@ -886,7 +886,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2;
(1 row) (1 row)
-- by default, user-defined ones cannot -- by default, user-defined ones cannot
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
QUERY PLAN QUERY PLAN
----------------------------------------------------------- -----------------------------------------------------------
...@@ -904,7 +904,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); ...@@ -904,7 +904,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
9 9
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
QUERY PLAN QUERY PLAN
----------------------------------------------------------- -----------------------------------------------------------
...@@ -927,7 +927,7 @@ ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_abs(int); ...@@ -927,7 +927,7 @@ ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_abs(int);
ALTER EXTENSION postgres_fdw ADD OPERATOR === (int, int); ALTER EXTENSION postgres_fdw ADD OPERATOR === (int, int);
ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw');
-- ... now they can be shipped -- ... now they can be shipped
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------
...@@ -944,7 +944,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); ...@@ -944,7 +944,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
9 9
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------
...@@ -969,7 +969,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; ...@@ -969,7 +969,7 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
ANALYZE ft4; ANALYZE ft4;
ANALYZE ft5; ANALYZE ft5;
-- join two tables -- join two tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -997,7 +997,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t ...@@ -997,7 +997,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t
(10 rows) (10 rows)
-- join three tables -- join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t3 ON (t3.c1 = t1.c1) ORDER BY t1.c3, t1.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t3 ON (t3.c1 = t1.c1) ORDER BY t1.c3, t1.c1 OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1028,7 +1028,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t ...@@ -1028,7 +1028,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t
(10 rows) (10 rows)
-- left outer join -- left outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1056,7 +1056,7 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1. ...@@ -1056,7 +1056,7 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.
(10 rows) (10 rows)
-- left outer join three tables -- left outer join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1086,7 +1086,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT ...@@ -1086,7 +1086,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT
-- left outer join + placement of clauses. -- left outer join + placement of clauses.
-- clauses within the nullable side are not pulled up, but top level clause on -- clauses within the nullable side are not pulled up, but top level clause on
-- non-nullable side is pushed into non-nullable side -- non-nullable side is pushed into non-nullable side
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) WHERE t1.c1 < 10; SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) WHERE t1.c1 < 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1107,7 +1107,7 @@ SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE ...@@ -1107,7 +1107,7 @@ SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE
-- clauses within the nullable side are not pulled up, but the top level clause -- clauses within the nullable side are not pulled up, but the top level clause
-- on nullable side is not pushed down into nullable side -- on nullable side is not pushed down into nullable side
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1)
WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10; WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10;
QUERY PLAN QUERY PLAN
...@@ -1129,7 +1129,7 @@ SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE ...@@ -1129,7 +1129,7 @@ SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE
(4 rows) (4 rows)
-- right outer join -- right outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2.c1, t1.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2.c1, t1.c1 OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1157,7 +1157,7 @@ SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2 ...@@ -1157,7 +1157,7 @@ SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2
(10 rows) (10 rows)
-- right outer join three tables -- right outer join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1185,7 +1185,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGH ...@@ -1185,7 +1185,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGH
(10 rows) (10 rows)
-- full outer join -- full outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1213,7 +1213,7 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1. ...@@ -1213,7 +1213,7 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.
(10 rows) (10 rows)
-- full outer join with restrictions on the joining relations -- full outer join with restrictions on the joining relations
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------
...@@ -1247,7 +1247,7 @@ SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL ...@@ -1247,7 +1247,7 @@ SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL
(8 rows) (8 rows)
-- full outer join + inner join -- full outer join + inner join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10; SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1275,7 +1275,7 @@ SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 a ...@@ -1275,7 +1275,7 @@ SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 a
(10 rows) (10 rows)
-- full outer join three tables -- full outer join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1303,7 +1303,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL ...@@ -1303,7 +1303,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL
(10 rows) (10 rows)
-- full outer join + right outer join -- full outer join + right outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1331,7 +1331,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT ...@@ -1331,7 +1331,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT
(10 rows) (10 rows)
-- right outer join + full outer join -- right outer join + full outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1359,7 +1359,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL ...@@ -1359,7 +1359,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL
(10 rows) (10 rows)
-- full outer join + left outer join -- full outer join + left outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1387,7 +1387,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT ...@@ -1387,7 +1387,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT
(10 rows) (10 rows)
-- left outer join + full outer join -- left outer join + full outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1415,7 +1415,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL ...@@ -1415,7 +1415,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL
(10 rows) (10 rows)
-- right outer join + left outer join -- right outer join + left outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1443,7 +1443,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT ...@@ -1443,7 +1443,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT
(10 rows) (10 rows)
-- left outer join + right outer join -- left outer join + right outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1471,7 +1471,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT ...@@ -1471,7 +1471,7 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT
(10 rows) (10 rows)
-- full outer join + WHERE clause, only matched rows -- full outer join + WHERE clause, only matched rows
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1503,7 +1503,7 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 ...@@ -1503,7 +1503,7 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1
-- join two tables with FOR UPDATE clause -- join two tables with FOR UPDATE clause
-- tests whole-row reference for row marks -- tests whole-row reference for row marks
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE OF t1; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE OF t1;
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1547,7 +1547,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t ...@@ -1547,7 +1547,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t
110 | 110 110 | 110
(10 rows) (10 rows)
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE;
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1592,7 +1592,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t ...@@ -1592,7 +1592,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t
(10 rows) (10 rows)
-- join two tables with FOR SHARE clause -- join two tables with FOR SHARE clause
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE OF t1; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE OF t1;
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1636,7 +1636,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t ...@@ -1636,7 +1636,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t
110 | 110 110 | 110
(10 rows) (10 rows)
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE;
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1681,7 +1681,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t ...@@ -1681,7 +1681,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t
(10 rows) (10 rows)
-- join in CTE -- join in CTE
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1 OFFSET 100 LIMIT 10; WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------
...@@ -1715,7 +1715,7 @@ WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ...@@ -1715,7 +1715,7 @@ WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2
(10 rows) (10 rows)
-- ctid with whole-row reference -- ctid with whole-row reference
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1728,7 +1728,7 @@ SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER B ...@@ -1728,7 +1728,7 @@ SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER B
(6 rows) (6 rows)
-- SEMI JOIN, not pushed down -- SEMI JOIN, not pushed down
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
...@@ -1763,7 +1763,7 @@ SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ...@@ -1763,7 +1763,7 @@ SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1)
(10 rows) (10 rows)
-- ANTI JOIN, not pushed down -- ANTI JOIN, not pushed down
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------
...@@ -1798,7 +1798,7 @@ SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2 ...@@ -1798,7 +1798,7 @@ SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2
(10 rows) (10 rows)
-- CROSS JOIN, not pushed down -- CROSS JOIN, not pushed down
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------- ---------------------------------------------------------------------
...@@ -1835,7 +1835,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 1 ...@@ -1835,7 +1835,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 1
(10 rows) (10 rows)
-- different server, not pushed down. No result expected. -- different server, not pushed down. No result expected.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------
...@@ -1861,7 +1861,7 @@ SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t ...@@ -1861,7 +1861,7 @@ SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t
-- unsafe join conditions (c8 has a UDT), not pushed down. Practically a CROSS -- unsafe join conditions (c8 has a UDT), not pushed down. Practically a CROSS
-- JOIN since c8 in both tables has same value. -- JOIN since c8 in both tables has same value.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------- -------------------------------------------------------------------------
...@@ -1903,7 +1903,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1. ...@@ -1903,7 +1903,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.
(10 rows) (10 rows)
-- unsafe conditions on one side (c8 has a UDT), not pushed down. -- unsafe conditions on one side (c8 has a UDT), not pushed down.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = 'foo' ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = 'foo' ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
...@@ -1945,7 +1945,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 ...@@ -1945,7 +1945,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8
-- in the SELECT clause. In this test unsafe clause needs to have column -- in the SELECT clause. In this test unsafe clause needs to have column
-- references from both joining sides so that the clause is not pushed down -- references from both joining sides so that the clause is not pushed down
-- into one of the joining sides. -- into one of the joining sides.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.c8 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.c8 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -1977,7 +1977,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2. ...@@ -1977,7 +1977,7 @@ SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.
(10 rows) (10 rows)
-- Aggregate after UNION, for testing setrefs -- Aggregate after UNION, for testing setrefs
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) UNION SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) AS t (t1c1, t2c1) GROUP BY t1c1 ORDER BY t1c1 OFFSET 100 LIMIT 10; SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) UNION SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) AS t (t1c1, t2c1) GROUP BY t1c1 ORDER BY t1c1 OFFSET 100 LIMIT 10;
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -2019,7 +2019,7 @@ SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ...@@ -2019,7 +2019,7 @@ SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2
(10 rows) (10 rows)
-- join with lateral reference -- join with lateral reference
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10;
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -2053,159 +2053,10 @@ SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ...@@ -2053,159 +2053,10 @@ SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM
1 1
(10 rows) (10 rows)
-- create another user for permission, user mapping, effective user tests
CREATE USER view_owner;
-- grant privileges on ft4 and ft5 to view_owner
GRANT ALL ON ft4 TO view_owner;
GRANT ALL ON ft5 TO view_owner;
-- prepare statement with current session user
PREPARE join_stmt AS SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
Remote SQL: SELECT r1.c1, r2.c1 FROM ("S 1"."T 3" r1 LEFT JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) ORDER BY r1.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST
(6 rows)
EXECUTE join_stmt;
c1 | c1
----+----
22 |
24 | 24
26 |
28 |
30 | 30
32 |
34 |
36 | 36
38 |
40 |
(10 rows)
-- change the session user to view_owner and execute the statement. Because of
-- change in session user, the plan should get invalidated and created again.
-- The join will not be pushed down since the joining relations do not have a
-- valid user mapping.
SET SESSION ROLE view_owner;
EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
QUERY PLAN
------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Sort
Output: t1.c1, t2.c1
Sort Key: t1.c1, t2.c1
-> Hash Left Join
Output: t1.c1, t2.c1
Hash Cond: (t1.c1 = t2.c1)
-> Foreign Scan on public.ft4 t1
Output: t1.c1, t1.c2, t1.c3
Remote SQL: SELECT c1 FROM "S 1"."T 3"
-> Hash
Output: t2.c1
-> Foreign Scan on public.ft5 t2
Output: t2.c1
Remote SQL: SELECT c1 FROM "S 1"."T 4"
(16 rows)
RESET ROLE;
DEALLOCATE join_stmt;
CREATE VIEW v_ft5 AS SELECT * FROM ft5;
-- change owner of v_ft5 to view_owner so that the effective user for scan on
-- ft5 is view_owner and not the current user.
ALTER VIEW v_ft5 OWNER TO view_owner;
-- create a public user mapping for loopback server
-- drop user mapping for current_user.
DROP USER MAPPING FOR CURRENT_USER SERVER loopback;
CREATE USER MAPPING FOR PUBLIC SERVER loopback;
-- different effective user for permission check, but same user mapping for the
-- joining sides, join pushed down, no result expected.
PREPARE join_stmt AS SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN v_ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, ft5.c1
-> Foreign Scan
Output: t1.c1, ft5.c1
Relations: (public.ft5 t1) INNER JOIN (public.ft5)
Remote SQL: SELECT r1.c1, r6.c1 FROM ("S 1"."T 4" r1 INNER JOIN "S 1"."T 4" r6 ON (((r1.c1 = r6.c1)))) ORDER BY r1.c1 ASC NULLS LAST
(6 rows)
EXECUTE join_stmt;
c1 | c1
----+----
(0 rows)
-- create user mapping for view_owner and execute the prepared statement
-- the join should not be pushed down since joining relations now use two
-- different user mappings
CREATE USER MAPPING FOR view_owner SERVER loopback;
EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
QUERY PLAN
---------------------------------------------------------------------------------------
Limit
Output: t1.c1, ft5.c1
-> Merge Join
Output: t1.c1, ft5.c1
Merge Cond: (t1.c1 = ft5.c1)
-> Foreign Scan on public.ft5 t1
Output: t1.c1, t1.c2, t1.c3
Remote SQL: SELECT c1 FROM "S 1"."T 4" ORDER BY c1 ASC NULLS LAST
-> Materialize
Output: ft5.c1, ft5.c2, ft5.c3
-> Foreign Scan on public.ft5
Output: ft5.c1, ft5.c2, ft5.c3
Remote SQL: SELECT c1 FROM "S 1"."T 4" ORDER BY c1 ASC NULLS LAST
(13 rows)
EXECUTE join_stmt;
c1 | c1
----+----
(0 rows)
-- If a sub-join can't be pushed down, upper level join shouldn't be either.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1 FROM (ft5 t1 JOIN v_ft5 t2 ON (t1.c1 = t2.c1)) left join (ft5 t3 JOIN v_ft5 t4 ON (t3.c1 = t4.c1)) ON (t1.c1 = t3.c1);
QUERY PLAN
------------------------------------------------------------------
Hash Join
Output: t1.c1, ft5.c1
Hash Cond: (t1.c1 = ft5.c1)
-> Hash Right Join
Output: t1.c1
Hash Cond: (t3.c1 = t1.c1)
-> Hash Join
Output: t3.c1
Hash Cond: (t3.c1 = ft5_1.c1)
-> Foreign Scan on public.ft5 t3
Output: t3.c1, t3.c2, t3.c3
Remote SQL: SELECT c1 FROM "S 1"."T 4"
-> Hash
Output: ft5_1.c1
-> Foreign Scan on public.ft5 ft5_1
Output: ft5_1.c1
Remote SQL: SELECT c1 FROM "S 1"."T 4"
-> Hash
Output: t1.c1
-> Foreign Scan on public.ft5 t1
Output: t1.c1
Remote SQL: SELECT c1 FROM "S 1"."T 4"
-> Hash
Output: ft5.c1
-> Foreign Scan on public.ft5
Output: ft5.c1
Remote SQL: SELECT c1 FROM "S 1"."T 4"
(27 rows)
-- non-Var items in targelist of the nullable rel of a join preventing -- non-Var items in targelist of the nullable rel of a join preventing
-- push-down in some cases -- push-down in some cases
-- unable to push {ft1, ft2} -- unable to push {ft1, ft2}
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 ON (q.a = ft2.c1) WHERE ft2.c1 BETWEEN 10 AND 15; SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 ON (q.a = ft2.c1) WHERE ft2.c1 BETWEEN 10 AND 15;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------
...@@ -2234,7 +2085,7 @@ SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 O ...@@ -2234,7 +2085,7 @@ SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 O
(6 rows) (6 rows)
-- ok to push {ft1, ft2} but not {ft1, ft2, ft4} -- ok to push {ft1, ft2} but not {ft1, ft2, ft4}
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT JOIN ft2 ON (ft1.c1 = ft2.c1) WHERE ft1.c1 = 12) q(a, b, c) ON (ft4.c1 = q.b) WHERE ft4.c1 BETWEEN 10 AND 15; SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT JOIN ft2 ON (ft1.c1 = ft2.c1) WHERE ft1.c1 = 12) q(a, b, c) ON (ft4.c1 = q.b) WHERE ft4.c1 BETWEEN 10 AND 15;
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
...@@ -2260,15 +2111,13 @@ SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT ...@@ -2260,15 +2111,13 @@ SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT
14 | | | 14 | | |
(3 rows) (3 rows)
-- recreate the dropped user mapping for further tests
CREATE USER MAPPING FOR CURRENT_USER SERVER loopback;
DROP USER MAPPING FOR PUBLIC SERVER loopback;
-- join with nullable side with some columns with null values -- join with nullable side with some columns with null values
UPDATE ft5 SET c3 = null where c1 % 9 = 0; UPDATE ft5 SET c3 = null where c1 % 9 = 0;
EXPLAIN VERBOSE SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5.c1 = ft4.c1 WHERE ft4.c1 BETWEEN 10 and 30 ORDER BY ft5.c1, ft4.c1; EXPLAIN (VERBOSE, COSTS OFF)
SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5.c1 = ft4.c1 WHERE ft4.c1 BETWEEN 10 and 30 ORDER BY ft5.c1, ft4.c1;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan (cost=100.00..106.81 rows=7 width=62) Foreign Scan
Output: ft5.*, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 Output: ft5.*, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2
Relations: (public.ft5) INNER JOIN (public.ft4) Relations: (public.ft5) INNER JOIN (public.ft4)
Remote SQL: SELECT CASE WHEN (r1.*)::text IS NOT NULL THEN ROW(r1.c1, r1.c2, r1.c3) END, r1.c1, r1.c2, r1.c3, r2.c1, r2.c2 FROM ("S 1"."T 4" r1 INNER JOIN "S 1"."T 3" r2 ON (((r1.c1 = r2.c1)) AND ((r2.c1 >= 10)) AND ((r2.c1 <= 30)))) ORDER BY r1.c1 ASC NULLS LAST Remote SQL: SELECT CASE WHEN (r1.*)::text IS NOT NULL THEN ROW(r1.c1, r1.c2, r1.c3) END, r1.c1, r1.c2, r1.c3, r2.c1, r2.c2 FROM ("S 1"."T 4" r1 INNER JOIN "S 1"."T 3" r2 ON (((r1.c1 = r2.c1)) AND ((r2.c1 >= 10)) AND ((r2.c1 <= 30)))) ORDER BY r1.c1 ASC NULLS LAST
...@@ -2283,12 +2132,154 @@ SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5 ...@@ -2283,12 +2132,154 @@ SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5
(30,31,AAA030) | 30 | 31 | AAA030 | 30 | 31 (30,31,AAA030) | 30 | 31 | AAA030 | 30 | 31
(4 rows) (4 rows)
-- check join pushdown in situations where multiple userids are involved
CREATE ROLE regress_view_owner;
CREATE USER MAPPING FOR regress_view_owner SERVER loopback;
GRANT SELECT ON ft4 TO regress_view_owner;
GRANT SELECT ON ft5 TO regress_view_owner;
CREATE VIEW v4 AS SELECT * FROM ft4;
CREATE VIEW v5 AS SELECT * FROM ft5;
ALTER VIEW v5 OWNER TO regress_view_owner;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can't be pushed down, different view owners
QUERY PLAN
----------------------------------------------------------------------
Limit
Output: ft4.c1, ft5.c2, ft5.c1
-> Sort
Output: ft4.c1, ft5.c2, ft5.c1
Sort Key: ft4.c1, ft5.c1
-> Hash Left Join
Output: ft4.c1, ft5.c2, ft5.c1
Hash Cond: (ft4.c1 = ft5.c1)
-> Foreign Scan on public.ft4
Output: ft4.c1, ft4.c2, ft4.c3
Remote SQL: SELECT c1 FROM "S 1"."T 3"
-> Hash
Output: ft5.c2, ft5.c1
-> Foreign Scan on public.ft5
Output: ft5.c2, ft5.c1
Remote SQL: SELECT c1, c2 FROM "S 1"."T 4"
(16 rows)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
c1 | c2
----+----
22 |
24 | 25
26 |
28 |
30 | 31
32 |
34 |
36 | 37
38 |
40 |
(10 rows)
ALTER VIEW v4 OWNER TO regress_view_owner;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can be pushed down
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit
Output: ft4.c1, ft5.c2, ft5.c1
-> Foreign Scan
Output: ft4.c1, ft5.c2, ft5.c1
Relations: (public.ft4) LEFT JOIN (public.ft5)
Remote SQL: SELECT r6.c1, r9.c2, r9.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r9 ON (((r6.c1 = r9.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r9.c1 ASC NULLS LAST
(6 rows)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
c1 | c2
----+----
22 |
24 | 25
26 |
28 |
30 | 31
32 |
34 |
36 | 37
38 |
40 |
(10 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can't be pushed down, view owner not current user
QUERY PLAN
----------------------------------------------------------------------
Limit
Output: ft4.c1, t2.c2, t2.c1
-> Sort
Output: ft4.c1, t2.c2, t2.c1
Sort Key: ft4.c1, t2.c1
-> Hash Left Join
Output: ft4.c1, t2.c2, t2.c1
Hash Cond: (ft4.c1 = t2.c1)
-> Foreign Scan on public.ft4
Output: ft4.c1, ft4.c2, ft4.c3
Remote SQL: SELECT c1 FROM "S 1"."T 3"
-> Hash
Output: t2.c2, t2.c1
-> Foreign Scan on public.ft5 t2
Output: t2.c2, t2.c1
Remote SQL: SELECT c1, c2 FROM "S 1"."T 4"
(16 rows)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
c1 | c2
----+----
22 |
24 | 25
26 |
28 |
30 | 31
32 |
34 |
36 | 37
38 |
40 |
(10 rows)
ALTER VIEW v4 OWNER TO CURRENT_USER;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can be pushed down
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit
Output: ft4.c1, t2.c2, t2.c1
-> Foreign Scan
Output: ft4.c1, t2.c2, t2.c1
Relations: (public.ft4) LEFT JOIN (public.ft5 t2)
Remote SQL: SELECT r6.c1, r2.c2, r2.c1 FROM ("S 1"."T 3" r6 LEFT JOIN "S 1"."T 4" r2 ON (((r6.c1 = r2.c1)))) ORDER BY r6.c1 ASC NULLS LAST, r2.c1 ASC NULLS LAST
(6 rows)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
c1 | c2
----+----
22 |
24 | 25
26 |
28 |
30 | 31
32 |
34 |
36 | 37
38 |
40 |
(10 rows)
ALTER VIEW v4 OWNER TO regress_view_owner;
-- cleanup
DROP OWNED BY regress_view_owner;
DROP ROLE regress_view_owner;
-- =================================================================== -- ===================================================================
-- parameterized queries -- parameterized queries
-- =================================================================== -- ===================================================================
-- simple join -- simple join
PREPARE st1(int, int) AS SELECT t1.c3, t2.c3 FROM ft1 t1, ft2 t2 WHERE t1.c1 = $1 AND t2.c1 = $2; PREPARE st1(int, int) AS SELECT t1.c3, t2.c3 FROM ft1 t1, ft2 t2 WHERE t1.c1 = $1 AND t2.c1 = $2;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st1(1, 2); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st1(1, 2);
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------
Foreign Scan Foreign Scan
...@@ -2311,7 +2302,7 @@ EXECUTE st1(101, 101); ...@@ -2311,7 +2302,7 @@ EXECUTE st1(101, 101);
-- subquery using stable function (can't be sent to remote) -- subquery using stable function (can't be sent to remote)
PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1; PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st2(10, 20);
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------
Sort Sort
...@@ -2345,7 +2336,7 @@ EXECUTE st2(101, 121); ...@@ -2345,7 +2336,7 @@ EXECUTE st2(101, 121);
-- subquery using immutable function (can be sent to remote) -- subquery using immutable function (can be sent to remote)
PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1; PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st3(10, 20);
QUERY PLAN QUERY PLAN
----------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------
Sort Sort
...@@ -2377,7 +2368,7 @@ EXECUTE st3(20, 30); ...@@ -2377,7 +2368,7 @@ EXECUTE st3(20, 30);
-- custom plan should be chosen initially -- custom plan should be chosen initially
PREPARE st4(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 = $1; PREPARE st4(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 = $1;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2385,7 +2376,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); ...@@ -2385,7 +2376,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2393,7 +2384,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); ...@@ -2393,7 +2384,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2401,7 +2392,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); ...@@ -2401,7 +2392,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2409,7 +2400,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); ...@@ -2409,7 +2400,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(3 rows) (3 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2418,7 +2409,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); ...@@ -2418,7 +2409,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1);
(3 rows) (3 rows)
-- once we try it enough times, should switch to generic plan -- once we try it enough times, should switch to generic plan
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2428,7 +2419,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); ...@@ -2428,7 +2419,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1);
-- value of $1 should not be sent to remote -- value of $1 should not be sent to remote
PREPARE st5(user_enum,int) AS SELECT * FROM ft1 t1 WHERE c8 = $1 and c1 = $2; PREPARE st5(user_enum,int) AS SELECT * FROM ft1 t1 WHERE c8 = $1 and c1 = $2;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2437,7 +2428,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); ...@@ -2437,7 +2428,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(4 rows) (4 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2446,7 +2437,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); ...@@ -2446,7 +2437,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(4 rows) (4 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2455,7 +2446,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); ...@@ -2455,7 +2446,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(4 rows) (4 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2464,7 +2455,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); ...@@ -2464,7 +2455,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(4 rows) (4 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2473,7 +2464,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); ...@@ -2473,7 +2464,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1);
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1)) Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = 1))
(4 rows) (4 rows)
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1 Foreign Scan on public.ft1 t1
...@@ -2495,7 +2486,7 @@ DEALLOCATE st3; ...@@ -2495,7 +2486,7 @@ DEALLOCATE st3;
DEALLOCATE st4; DEALLOCATE st4;
DEALLOCATE st5; DEALLOCATE st5;
-- System columns, except ctid, should not be sent to remote -- System columns, except ctid, should not be sent to remote
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1; SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -2513,7 +2504,7 @@ SELECT * FROM ft1 t1 WHERE t1.tableoid = 'ft1'::regclass LIMIT 1; ...@@ -2513,7 +2504,7 @@ SELECT * FROM ft1 t1 WHERE t1.tableoid = 'ft1'::regclass LIMIT 1;
1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1; SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
...@@ -2530,7 +2521,7 @@ SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1; ...@@ -2530,7 +2521,7 @@ SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1;
ft1 | 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo ft1 | 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)'; SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)';
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
...@@ -2545,7 +2536,7 @@ SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)'; ...@@ -2545,7 +2536,7 @@ SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)';
2 | 2 | 00002 | Sat Jan 03 00:00:00 1970 PST | Sat Jan 03 00:00:00 1970 | 2 | 2 | foo 2 | 2 | 00002 | Sat Jan 03 00:00:00 1970 PST | Sat Jan 03 00:00:00 1970 | 2 | 2 | foo
(1 row) (1 row)
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT ctid, * FROM ft1 t1 LIMIT 1; SELECT ctid, * FROM ft1 t1 LIMIT 1;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------
...@@ -4344,7 +4335,7 @@ select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1; ...@@ -4344,7 +4335,7 @@ select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1;
-- Above DMLs add data with c6 as NULL in ft1, so test ORDER BY NULLS LAST and NULLs -- Above DMLs add data with c6 as NULL in ft1, so test ORDER BY NULLS LAST and NULLs
-- FIRST behavior here. -- FIRST behavior here.
-- ORDER BY DESC NULLS LAST options -- ORDER BY DESC NULLS LAST options
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------
Limit Limit
...@@ -4370,7 +4361,7 @@ SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10; ...@@ -4370,7 +4361,7 @@ SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10;
(10 rows) (10 rows)
-- ORDER BY DESC NULLS FIRST options -- ORDER BY DESC NULLS FIRST options
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
QUERY PLAN QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------
Limit Limit
...@@ -4396,7 +4387,7 @@ SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10; ...@@ -4396,7 +4387,7 @@ SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
(10 rows) (10 rows)
-- ORDER BY ASC NULLS FIRST options -- ORDER BY ASC NULLS FIRST options
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
QUERY PLAN QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------
Limit Limit
...@@ -4426,7 +4417,7 @@ SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10; ...@@ -4426,7 +4417,7 @@ SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
-- =================================================================== -- ===================================================================
-- Consistent check constraints provide consistent results -- Consistent check constraints provide consistent results
ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2positive CHECK (c2 >= 0); ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2positive CHECK (c2 >= 0);
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 < 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0;
QUERY PLAN QUERY PLAN
------------------------------------------------------------------- -------------------------------------------------------------------
Aggregate Aggregate
...@@ -4442,7 +4433,7 @@ SELECT count(*) FROM ft1 WHERE c2 < 0; ...@@ -4442,7 +4433,7 @@ SELECT count(*) FROM ft1 WHERE c2 < 0;
(1 row) (1 row)
SET constraint_exclusion = 'on'; SET constraint_exclusion = 'on';
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 < 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0;
QUERY PLAN QUERY PLAN
-------------------------------- --------------------------------
Aggregate Aggregate
...@@ -4470,7 +4461,7 @@ CONTEXT: Remote SQL command: UPDATE "S 1"."T 1" SET c2 = (- c2) WHERE (("C 1" = ...@@ -4470,7 +4461,7 @@ CONTEXT: Remote SQL command: UPDATE "S 1"."T 1" SET c2 = (- c2) WHERE (("C 1" =
ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive; ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive;
-- But inconsistent check constraints provide inconsistent results -- But inconsistent check constraints provide inconsistent results
ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0); ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0);
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 >= 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0;
QUERY PLAN QUERY PLAN
-------------------------------------------------------------------- --------------------------------------------------------------------
Aggregate Aggregate
...@@ -4486,7 +4477,7 @@ SELECT count(*) FROM ft1 WHERE c2 >= 0; ...@@ -4486,7 +4477,7 @@ SELECT count(*) FROM ft1 WHERE c2 >= 0;
(1 row) (1 row)
SET constraint_exclusion = 'on'; SET constraint_exclusion = 'on';
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 >= 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0;
QUERY PLAN QUERY PLAN
-------------------------------- --------------------------------
Aggregate Aggregate
...@@ -5867,6 +5858,3 @@ AND ftoptions @> array['fetch_size=60000']; ...@@ -5867,6 +5858,3 @@ AND ftoptions @> array['fetch_size=60000'];
(1 row) (1 row)
ROLLBACK; ROLLBACK;
-- Cleanup
DROP OWNED BY view_owner;
DROP USER view_owner;
...@@ -67,8 +67,6 @@ enum FdwScanPrivateIndex ...@@ -67,8 +67,6 @@ enum FdwScanPrivateIndex
FdwScanPrivateRetrievedAttrs, FdwScanPrivateRetrievedAttrs,
/* Integer representing the desired fetch_size */ /* Integer representing the desired fetch_size */
FdwScanPrivateFetchSize, FdwScanPrivateFetchSize,
/* Oid of user mapping to be used while connecting to the foreign server */
FdwScanPrivateUserMappingOid,
/* /*
* String describing join i.e. names of relations being joined and types * String describing join i.e. names of relations being joined and types
...@@ -1226,11 +1224,10 @@ postgresGetForeignPlan(PlannerInfo *root, ...@@ -1226,11 +1224,10 @@ postgresGetForeignPlan(PlannerInfo *root,
* Build the fdw_private list that will be available to the executor. * Build the fdw_private list that will be available to the executor.
* Items in the list must match order in enum FdwScanPrivateIndex. * Items in the list must match order in enum FdwScanPrivateIndex.
*/ */
fdw_private = list_make5(makeString(sql.data), fdw_private = list_make4(makeString(sql.data),
remote_conds, remote_conds,
retrieved_attrs, retrieved_attrs,
makeInteger(fpinfo->fetch_size), makeInteger(fpinfo->fetch_size));
makeInteger(foreignrel->umid));
if (foreignrel->reloptkind == RELOPT_JOINREL) if (foreignrel->reloptkind == RELOPT_JOINREL)
fdw_private = lappend(fdw_private, fdw_private = lappend(fdw_private,
makeString(fpinfo->relation_name->data)); makeString(fpinfo->relation_name->data));
...@@ -1262,7 +1259,11 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) ...@@ -1262,7 +1259,11 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan; ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
EState *estate = node->ss.ps.state; EState *estate = node->ss.ps.state;
PgFdwScanState *fsstate; PgFdwScanState *fsstate;
RangeTblEntry *rte;
Oid userid;
ForeignTable *table;
UserMapping *user; UserMapping *user;
int rtindex;
int numParams; int numParams;
/* /*
...@@ -1278,36 +1279,20 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) ...@@ -1278,36 +1279,20 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
node->fdw_state = (void *) fsstate; node->fdw_state = (void *) fsstate;
/* /*
* Obtain the foreign server where to connect and user mapping to use for * Identify which user to do the remote access as. This should match what
* connection. For base relations we obtain this information from * ExecCheckRTEPerms() does. In case of a join, use the lowest-numbered
* catalogs. For join relations, this information is frozen at the time of * member RTE as a representative; we would get the same result from any.
* planning to ensure that the join is safe to pushdown. In case the
* information goes stale between planning and execution, plan will be
* invalidated and replanned.
*/ */
if (fsplan->scan.scanrelid > 0) if (fsplan->scan.scanrelid > 0)
{ rtindex = fsplan->scan.scanrelid;
ForeignTable *table;
/*
* Identify which user to do the remote access as. This should match
* what ExecCheckRTEPerms() does.
*/
RangeTblEntry *rte = rt_fetch(fsplan->scan.scanrelid, estate->es_range_table);
Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
fsstate->rel = node->ss.ss_currentRelation;
table = GetForeignTable(RelationGetRelid(fsstate->rel));
user = GetUserMapping(userid, table->serverid);
}
else else
{ rtindex = bms_next_member(fsplan->fs_relids, -1);
Oid umid = intVal(list_nth(fsplan->fdw_private, FdwScanPrivateUserMappingOid)); rte = rt_fetch(rtindex, estate->es_range_table);
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
user = GetUserMappingById(umid); /* Get info about foreign table. */
Assert(fsplan->fs_server == user->serverid); table = GetForeignTable(rte->relid);
} user = GetUserMapping(userid, table->serverid);
/* /*
* Get connection to the foreign server. Connection manager will * Get connection to the foreign server. Connection manager will
...@@ -1344,9 +1329,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) ...@@ -1344,9 +1329,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
* into local representation and error reporting during that process. * into local representation and error reporting during that process.
*/ */
if (fsplan->scan.scanrelid > 0) if (fsplan->scan.scanrelid > 0)
{
fsstate->rel = node->ss.ss_currentRelation;
fsstate->tupdesc = RelationGetDescr(fsstate->rel); fsstate->tupdesc = RelationGetDescr(fsstate->rel);
}
else else
{
fsstate->rel = NULL;
fsstate->tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor; fsstate->tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
}
fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc); fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
...@@ -3965,16 +3956,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, ...@@ -3965,16 +3956,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
List *joinclauses; List *joinclauses;
List *otherclauses; List *otherclauses;
/*
* Core code may call GetForeignJoinPaths hook even when the join relation
* doesn't have a valid user mapping associated with it. See
* build_join_rel() for details. We can't push down such join, since there
* doesn't exist a user mapping which can be used to connect to the
* foreign server.
*/
if (!OidIsValid(joinrel->umid))
return false;
/* /*
* We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins. * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
* Constructing queries representing SEMI and ANTI joins is hard, hence * Constructing queries representing SEMI and ANTI joins is hard, hence
...@@ -4151,6 +4132,20 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, ...@@ -4151,6 +4132,20 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate || fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate ||
fpinfo_i->use_remote_estimate; fpinfo_i->use_remote_estimate;
/* Get user mapping */
if (fpinfo->use_remote_estimate)
{
if (fpinfo_o->use_remote_estimate)
fpinfo->user = fpinfo_o->user;
else
fpinfo->user = fpinfo_i->user;
}
else
fpinfo->user = NULL;
/* Get foreign server */
fpinfo->server = fpinfo_o->server;
/* /*
* Since both the joining relations come from the same server, the server * Since both the joining relations come from the same server, the server
* level options should have same value for both the relations. Pick from * level options should have same value for both the relations. Pick from
...@@ -4312,26 +4307,14 @@ postgresGetForeignJoinPaths(PlannerInfo *root, ...@@ -4312,26 +4307,14 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root); cost_qual_eval(&fpinfo->local_conds_cost, fpinfo->local_conds, root);
/* /*
* If we are going to estimate the costs using EXPLAIN, we will need * If we are going to estimate costs locally, estimate the join clause
* connection information. Fill it here. * selectivity here while we have special join info.
*/ */
if (fpinfo->use_remote_estimate) if (!fpinfo->use_remote_estimate)
fpinfo->user = GetUserMappingById(joinrel->umid);
else
{
fpinfo->user = NULL;
/*
* If we are going to estimate costs locally, estimate the join clause
* selectivity here while we have special join info.
*/
fpinfo->joinclause_sel = clauselist_selectivity(root, fpinfo->joinclauses, fpinfo->joinclause_sel = clauselist_selectivity(root, fpinfo->joinclauses,
0, fpinfo->jointype, 0, fpinfo->jointype,
extra->sjinfo); extra->sjinfo);
}
fpinfo->server = GetForeignServer(joinrel->serverid);
/* Estimate costs for bare join relation */ /* Estimate costs for bare join relation */
estimate_path_cost_size(root, joinrel, NIL, NIL, &rows, estimate_path_cost_size(root, joinrel, NIL, NIL, &rows,
&width, &startup_cost, &total_cost); &width, &startup_cost, &total_cost);
......
...@@ -197,23 +197,23 @@ ALTER FOREIGN TABLE ft2 OPTIONS (use_remote_estimate 'true'); ...@@ -197,23 +197,23 @@ ALTER FOREIGN TABLE ft2 OPTIONS (use_remote_estimate 'true');
-- simple queries -- simple queries
-- =================================================================== -- ===================================================================
-- single table without alias -- single table without alias
EXPLAIN (COSTS false) SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10; EXPLAIN (COSTS OFF) SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10;
SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10; SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10;
-- single table with alias - also test that tableoid sort is not pushed to remote side -- single table with alias - also test that tableoid sort is not pushed to remote side
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10; SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
-- whole-row reference -- whole-row reference
EXPLAIN (VERBOSE, COSTS false) SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
-- empty result -- empty result
SELECT * FROM ft1 WHERE false; SELECT * FROM ft1 WHERE false;
-- with WHERE clause -- with WHERE clause
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1'; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1'; SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
-- with FOR UPDATE/SHARE -- with FOR UPDATE/SHARE
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE;
SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE; SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE;
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE;
SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE; SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE;
-- aggregate -- aggregate
SELECT COUNT(*) FROM ft1 t1; SELECT COUNT(*) FROM ft1 t1;
...@@ -229,27 +229,27 @@ SELECT 'fixed', NULL FROM ft1 t1 WHERE c1 = 1; ...@@ -229,27 +229,27 @@ SELECT 'fixed', NULL FROM ft1 t1 WHERE c1 = 1;
SET enable_hashjoin TO false; SET enable_hashjoin TO false;
SET enable_nestloop TO false; SET enable_nestloop TO false;
-- inner join; expressions in the clauses appear in the equivalence class list -- inner join; expressions in the clauses appear in the equivalence class list
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10;
SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10;
-- outer join; expressions in the clauses do not appear in equivalence class -- outer join; expressions in the clauses do not appear in equivalence class
-- list but no output change as compared to the previous query -- list but no output change as compared to the previous query
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10;
SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10;
-- A join between local table and foreign join. ORDER BY clause is added to the -- A join between local table and foreign join. ORDER BY clause is added to the
-- foreign join so that the local table can be joined using merge join strategy. -- foreign join so that the local table can be joined using merge join strategy.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
-- Test similar to above, except that the full join prevents any equivalence -- Test similar to above, except that the full join prevents any equivalence
-- classes from being merged. This produces single relation equivalence classes -- classes from being merged. This produces single relation equivalence classes
-- included in join restrictions. -- included in join restrictions.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
-- Test similar to above with all full outer joins -- Test similar to above with all full outer joins
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10;
RESET enable_hashjoin; RESET enable_hashjoin;
...@@ -258,25 +258,25 @@ RESET enable_nestloop; ...@@ -258,25 +258,25 @@ RESET enable_nestloop;
-- =================================================================== -- ===================================================================
-- WHERE with remotely-executable conditions -- WHERE with remotely-executable conditions
-- =================================================================== -- ===================================================================
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- Var, OpExpr(b), Const EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- Var, OpExpr(b), Const
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 = 0; -- BoolExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 = 0; -- BoolExpr
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- NullTest EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- NullTest
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r) EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r)
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote
-- parameterized remote path for foreign table -- parameterized remote path for foreign table
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM "S 1"."T 1" a, ft2 b WHERE a."C 1" = 47 AND b.c1 = a.c2; SELECT * FROM "S 1"."T 1" a, ft2 b WHERE a."C 1" = 47 AND b.c1 = a.c2;
SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2; SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
-- check both safe and unsafe join conditions -- check both safe and unsafe join conditions
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft2 a, ft2 b SELECT * FROM ft2 a, ft2 b
WHERE a.c2 = 6 AND b.c1 = a.c1 AND a.c8 = 'foo' AND b.c7 = upper(a.c7); WHERE a.c2 = 6 AND b.c1 = a.c1 AND a.c8 = 'foo' AND b.c7 = upper(a.c7);
SELECT * FROM ft2 a, ft2 b SELECT * FROM ft2 a, ft2 b
...@@ -286,9 +286,9 @@ SELECT * FROM ft1 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft2 WHERE c1 < 5)); ...@@ -286,9 +286,9 @@ SELECT * FROM ft1 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft2 WHERE c1 < 5));
SELECT * FROM ft2 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft1 WHERE c1 < 5)); SELECT * FROM ft2 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft1 WHERE c1 < 5));
-- we should not push order by clause with volatile expressions or unsafe -- we should not push order by clause with volatile expressions or unsafe
-- collations -- collations
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft2 ORDER BY ft2.c1, random(); SELECT * FROM ft2 ORDER BY ft2.c1, random();
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft2 ORDER BY ft2.c1, ft2.c3 collate "C"; SELECT * FROM ft2 ORDER BY ft2.c1, ft2.c3 collate "C";
-- user-defined operator/function -- user-defined operator/function
...@@ -305,18 +305,18 @@ CREATE OPERATOR === ( ...@@ -305,18 +305,18 @@ CREATE OPERATOR === (
); );
-- built-in operators and functions can be shipped for remote execution -- built-in operators and functions can be shipped for remote execution
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2);
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2);
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2;
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2;
-- by default, user-defined ones cannot -- by default, user-defined ones cannot
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
...@@ -326,10 +326,10 @@ ALTER EXTENSION postgres_fdw ADD OPERATOR === (int, int); ...@@ -326,10 +326,10 @@ ALTER EXTENSION postgres_fdw ADD OPERATOR === (int, int);
ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw');
-- ... now they can be shipped -- ... now they can be shipped
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
...@@ -342,247 +342,232 @@ ANALYZE ft4; ...@@ -342,247 +342,232 @@ ANALYZE ft4;
ANALYZE ft5; ANALYZE ft5;
-- join two tables -- join two tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
-- join three tables -- join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t3 ON (t3.c1 = t1.c1) ORDER BY t1.c3, t1.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t3 ON (t3.c1 = t1.c1) ORDER BY t1.c3, t1.c1 OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t3 ON (t3.c1 = t1.c1) ORDER BY t1.c3, t1.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) JOIN ft4 t3 ON (t3.c1 = t1.c1) ORDER BY t1.c3, t1.c1 OFFSET 10 LIMIT 10;
-- left outer join -- left outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
-- left outer join three tables -- left outer join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- left outer join + placement of clauses. -- left outer join + placement of clauses.
-- clauses within the nullable side are not pulled up, but top level clause on -- clauses within the nullable side are not pulled up, but top level clause on
-- non-nullable side is pushed into non-nullable side -- non-nullable side is pushed into non-nullable side
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) WHERE t1.c1 < 10; SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) WHERE t1.c1 < 10;
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) WHERE t1.c1 < 10; SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) WHERE t1.c1 < 10;
-- clauses within the nullable side are not pulled up, but the top level clause -- clauses within the nullable side are not pulled up, but the top level clause
-- on nullable side is not pushed down into nullable side -- on nullable side is not pushed down into nullable side
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1)
WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10; WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10;
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1) SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM ft4 t1 LEFT JOIN (SELECT * FROM ft5 WHERE c1 < 10) t2 ON (t1.c1 = t2.c1)
WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10; WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10;
-- right outer join -- right outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2.c1, t1.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2.c1, t1.c1 OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2.c1, t1.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft5 t1 RIGHT JOIN ft4 t2 ON (t1.c1 = t2.c1) ORDER BY t2.c1, t1.c1 OFFSET 10 LIMIT 10;
-- right outer join three tables -- right outer join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- full outer join -- full outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10;
-- full outer join with restrictions on the joining relations -- full outer join with restrictions on the joining relations
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1;
SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1;
-- full outer join + inner join -- full outer join + inner join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10; SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10;
SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10; SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10;
-- full outer join three tables -- full outer join three tables
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- full outer join + right outer join -- full outer join + right outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- right outer join + full outer join -- right outer join + full outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- full outer join + left outer join -- full outer join + left outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- left outer join + full outer join -- left outer join + full outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- right outer join + left outer join -- right outer join + left outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 RIGHT JOIN ft2 t2 ON (t1.c1 = t2.c1) LEFT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- left outer join + right outer join -- left outer join + right outer join
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT JOIN ft4 t3 ON (t2.c1 = t3.c1) OFFSET 10 LIMIT 10;
-- full outer join + WHERE clause, only matched rows -- full outer join + WHERE clause, only matched rows
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
-- join two tables with FOR UPDATE clause -- join two tables with FOR UPDATE clause
-- tests whole-row reference for row marks -- tests whole-row reference for row marks
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE OF t1; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE OF t1;
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE OF t1; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE OF t1;
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE;
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR UPDATE;
-- join two tables with FOR SHARE clause -- join two tables with FOR SHARE clause
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE OF t1; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE OF t1;
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE OF t1; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE OF t1;
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE;
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10 FOR SHARE;
-- join in CTE -- join in CTE
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1 OFFSET 100 LIMIT 10; WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1 OFFSET 100 LIMIT 10;
WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1 OFFSET 100 LIMIT 10; WITH t (c1_1, c1_3, c2_1) AS (SELECT t1.c1, t1.c3, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1 OFFSET 100 LIMIT 10;
-- ctid with whole-row reference -- ctid with whole-row reference
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
-- SEMI JOIN, not pushed down -- SEMI JOIN, not pushed down
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10;
-- ANTI JOIN, not pushed down -- ANTI JOIN, not pushed down
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10;
-- CROSS JOIN, not pushed down -- CROSS JOIN, not pushed down
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
-- different server, not pushed down. No result expected. -- different server, not pushed down. No result expected.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN ft6 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
-- unsafe join conditions (c8 has a UDT), not pushed down. Practically a CROSS -- unsafe join conditions (c8 has a UDT), not pushed down. Practically a CROSS
-- JOIN since c8 in both tables has same value. -- JOIN since c8 in both tables has same value.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c8 = t2.c8) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
-- unsafe conditions on one side (c8 has a UDT), not pushed down. -- unsafe conditions on one side (c8 has a UDT), not pushed down.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = 'foo' ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = 'foo' ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = 'foo' ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = 'foo' ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
-- join where unsafe to pushdown condition in WHERE clause has a column not -- join where unsafe to pushdown condition in WHERE clause has a column not
-- in the SELECT clause. In this test unsafe clause needs to have column -- in the SELECT clause. In this test unsafe clause needs to have column
-- references from both joining sides so that the clause is not pushed down -- references from both joining sides so that the clause is not pushed down
-- into one of the joining sides. -- into one of the joining sides.
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.c8 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.c8 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.c8 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE t1.c8 = t2.c8 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
-- Aggregate after UNION, for testing setrefs -- Aggregate after UNION, for testing setrefs
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) UNION SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) AS t (t1c1, t2c1) GROUP BY t1c1 ORDER BY t1c1 OFFSET 100 LIMIT 10; SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) UNION SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) AS t (t1c1, t2c1) GROUP BY t1c1 ORDER BY t1c1 OFFSET 100 LIMIT 10;
SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) UNION SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) AS t (t1c1, t2c1) GROUP BY t1c1 ORDER BY t1c1 OFFSET 100 LIMIT 10; SELECT t1c1, avg(t1c1 + t2c1) FROM (SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) UNION SELECT t1.c1, t2.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1)) AS t (t1c1, t2c1) GROUP BY t1c1 ORDER BY t1c1 OFFSET 100 LIMIT 10;
-- join with lateral reference -- join with lateral reference
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10;
SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10;
-- create another user for permission, user mapping, effective user tests
CREATE USER view_owner;
-- grant privileges on ft4 and ft5 to view_owner
GRANT ALL ON ft4 TO view_owner;
GRANT ALL ON ft5 TO view_owner;
-- prepare statement with current session user
PREPARE join_stmt AS SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
EXECUTE join_stmt;
-- change the session user to view_owner and execute the statement. Because of
-- change in session user, the plan should get invalidated and created again.
-- The join will not be pushed down since the joining relations do not have a
-- valid user mapping.
SET SESSION ROLE view_owner;
EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
RESET ROLE;
DEALLOCATE join_stmt;
CREATE VIEW v_ft5 AS SELECT * FROM ft5;
-- change owner of v_ft5 to view_owner so that the effective user for scan on
-- ft5 is view_owner and not the current user.
ALTER VIEW v_ft5 OWNER TO view_owner;
-- create a public user mapping for loopback server
-- drop user mapping for current_user.
DROP USER MAPPING FOR CURRENT_USER SERVER loopback;
CREATE USER MAPPING FOR PUBLIC SERVER loopback;
-- different effective user for permission check, but same user mapping for the
-- joining sides, join pushed down, no result expected.
PREPARE join_stmt AS SELECT t1.c1, t2.c1 FROM ft5 t1 JOIN v_ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
EXECUTE join_stmt;
-- create user mapping for view_owner and execute the prepared statement
-- the join should not be pushed down since joining relations now use two
-- different user mappings
CREATE USER MAPPING FOR view_owner SERVER loopback;
EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
EXECUTE join_stmt;
-- If a sub-join can't be pushed down, upper level join shouldn't be either.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1 FROM (ft5 t1 JOIN v_ft5 t2 ON (t1.c1 = t2.c1)) left join (ft5 t3 JOIN v_ft5 t4 ON (t3.c1 = t4.c1)) ON (t1.c1 = t3.c1);
-- non-Var items in targelist of the nullable rel of a join preventing -- non-Var items in targelist of the nullable rel of a join preventing
-- push-down in some cases -- push-down in some cases
-- unable to push {ft1, ft2} -- unable to push {ft1, ft2}
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 ON (q.a = ft2.c1) WHERE ft2.c1 BETWEEN 10 AND 15; SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 ON (q.a = ft2.c1) WHERE ft2.c1 BETWEEN 10 AND 15;
SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 ON (q.a = ft2.c1) WHERE ft2.c1 BETWEEN 10 AND 15; SELECT q.a, ft2.c1 FROM (SELECT 13 FROM ft1 WHERE c1 = 13) q(a) RIGHT JOIN ft2 ON (q.a = ft2.c1) WHERE ft2.c1 BETWEEN 10 AND 15;
-- ok to push {ft1, ft2} but not {ft1, ft2, ft4} -- ok to push {ft1, ft2} but not {ft1, ft2, ft4}
EXPLAIN (COSTS false, VERBOSE) EXPLAIN (VERBOSE, COSTS OFF)
SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT JOIN ft2 ON (ft1.c1 = ft2.c1) WHERE ft1.c1 = 12) q(a, b, c) ON (ft4.c1 = q.b) WHERE ft4.c1 BETWEEN 10 AND 15; SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT JOIN ft2 ON (ft1.c1 = ft2.c1) WHERE ft1.c1 = 12) q(a, b, c) ON (ft4.c1 = q.b) WHERE ft4.c1 BETWEEN 10 AND 15;
SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT JOIN ft2 ON (ft1.c1 = ft2.c1) WHERE ft1.c1 = 12) q(a, b, c) ON (ft4.c1 = q.b) WHERE ft4.c1 BETWEEN 10 AND 15; SELECT ft4.c1, q.* FROM ft4 LEFT JOIN (SELECT 13, ft1.c1, ft2.c1 FROM ft1 RIGHT JOIN ft2 ON (ft1.c1 = ft2.c1) WHERE ft1.c1 = 12) q(a, b, c) ON (ft4.c1 = q.b) WHERE ft4.c1 BETWEEN 10 AND 15;
-- recreate the dropped user mapping for further tests
CREATE USER MAPPING FOR CURRENT_USER SERVER loopback;
DROP USER MAPPING FOR PUBLIC SERVER loopback;
-- join with nullable side with some columns with null values -- join with nullable side with some columns with null values
UPDATE ft5 SET c3 = null where c1 % 9 = 0; UPDATE ft5 SET c3 = null where c1 % 9 = 0;
EXPLAIN VERBOSE SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5.c1 = ft4.c1 WHERE ft4.c1 BETWEEN 10 and 30 ORDER BY ft5.c1, ft4.c1; EXPLAIN (VERBOSE, COSTS OFF)
SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5.c1 = ft4.c1 WHERE ft4.c1 BETWEEN 10 and 30 ORDER BY ft5.c1, ft4.c1;
SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5.c1 = ft4.c1 WHERE ft4.c1 BETWEEN 10 and 30 ORDER BY ft5.c1, ft4.c1; SELECT ft5, ft5.c1, ft5.c2, ft5.c3, ft4.c1, ft4.c2 FROM ft5 left join ft4 on ft5.c1 = ft4.c1 WHERE ft4.c1 BETWEEN 10 and 30 ORDER BY ft5.c1, ft4.c1;
-- check join pushdown in situations where multiple userids are involved
CREATE ROLE regress_view_owner;
CREATE USER MAPPING FOR regress_view_owner SERVER loopback;
GRANT SELECT ON ft4 TO regress_view_owner;
GRANT SELECT ON ft5 TO regress_view_owner;
CREATE VIEW v4 AS SELECT * FROM ft4;
CREATE VIEW v5 AS SELECT * FROM ft5;
ALTER VIEW v5 OWNER TO regress_view_owner;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can't be pushed down, different view owners
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
ALTER VIEW v4 OWNER TO regress_view_owner;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can be pushed down
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN v5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can't be pushed down, view owner not current user
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
ALTER VIEW v4 OWNER TO CURRENT_USER;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; -- can be pushed down
SELECT t1.c1, t2.c2 FROM v4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
ALTER VIEW v4 OWNER TO regress_view_owner;
-- cleanup
DROP OWNED BY regress_view_owner;
DROP ROLE regress_view_owner;
-- =================================================================== -- ===================================================================
-- parameterized queries -- parameterized queries
-- =================================================================== -- ===================================================================
-- simple join -- simple join
PREPARE st1(int, int) AS SELECT t1.c3, t2.c3 FROM ft1 t1, ft2 t2 WHERE t1.c1 = $1 AND t2.c1 = $2; PREPARE st1(int, int) AS SELECT t1.c3, t2.c3 FROM ft1 t1, ft2 t2 WHERE t1.c1 = $1 AND t2.c1 = $2;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st1(1, 2); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st1(1, 2);
EXECUTE st1(1, 1); EXECUTE st1(1, 1);
EXECUTE st1(101, 101); EXECUTE st1(101, 101);
-- subquery using stable function (can't be sent to remote) -- subquery using stable function (can't be sent to remote)
PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1; PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st2(10, 20);
EXECUTE st2(10, 20); EXECUTE st2(10, 20);
EXECUTE st2(101, 121); EXECUTE st2(101, 121);
-- subquery using immutable function (can be sent to remote) -- subquery using immutable function (can be sent to remote)
PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1; PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st3(10, 20);
EXECUTE st3(10, 20); EXECUTE st3(10, 20);
EXECUTE st3(20, 30); EXECUTE st3(20, 30);
-- custom plan should be chosen initially -- custom plan should be chosen initially
PREPARE st4(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 = $1; PREPARE st4(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 = $1;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
-- once we try it enough times, should switch to generic plan -- once we try it enough times, should switch to generic plan
EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
-- value of $1 should not be sent to remote -- value of $1 should not be sent to remote
PREPARE st5(user_enum,int) AS SELECT * FROM ft1 t1 WHERE c8 = $1 and c1 = $2; PREPARE st5(user_enum,int) AS SELECT * FROM ft1 t1 WHERE c8 = $1 and c1 = $2;
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
EXECUTE st5('foo', 1); EXECUTE st5('foo', 1);
-- cleanup -- cleanup
...@@ -593,16 +578,16 @@ DEALLOCATE st4; ...@@ -593,16 +578,16 @@ DEALLOCATE st4;
DEALLOCATE st5; DEALLOCATE st5;
-- System columns, except ctid, should not be sent to remote -- System columns, except ctid, should not be sent to remote
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1; SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1;
SELECT * FROM ft1 t1 WHERE t1.tableoid = 'ft1'::regclass LIMIT 1; SELECT * FROM ft1 t1 WHERE t1.tableoid = 'ft1'::regclass LIMIT 1;
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1; SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1;
SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1; SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1;
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)'; SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)';
SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)'; SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)';
EXPLAIN (VERBOSE, COSTS false) EXPLAIN (VERBOSE, COSTS OFF)
SELECT ctid, * FROM ft1 t1 LIMIT 1; SELECT ctid, * FROM ft1 t1 LIMIT 1;
SELECT ctid, * FROM ft1 t1 LIMIT 1; SELECT ctid, * FROM ft1 t1 LIMIT 1;
...@@ -764,13 +749,13 @@ select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1; ...@@ -764,13 +749,13 @@ select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1;
-- Above DMLs add data with c6 as NULL in ft1, so test ORDER BY NULLS LAST and NULLs -- Above DMLs add data with c6 as NULL in ft1, so test ORDER BY NULLS LAST and NULLs
-- FIRST behavior here. -- FIRST behavior here.
-- ORDER BY DESC NULLS LAST options -- ORDER BY DESC NULLS LAST options
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10;
SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10; SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10;
-- ORDER BY DESC NULLS FIRST options -- ORDER BY DESC NULLS FIRST options
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10; SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
-- ORDER BY ASC NULLS FIRST options -- ORDER BY ASC NULLS FIRST options
EXPLAIN (VERBOSE, COSTS false) SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10; EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10; SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
-- =================================================================== -- ===================================================================
...@@ -779,10 +764,10 @@ SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10; ...@@ -779,10 +764,10 @@ SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
-- Consistent check constraints provide consistent results -- Consistent check constraints provide consistent results
ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2positive CHECK (c2 >= 0); ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2positive CHECK (c2 >= 0);
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 < 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0;
SELECT count(*) FROM ft1 WHERE c2 < 0; SELECT count(*) FROM ft1 WHERE c2 < 0;
SET constraint_exclusion = 'on'; SET constraint_exclusion = 'on';
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 < 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0;
SELECT count(*) FROM ft1 WHERE c2 < 0; SELECT count(*) FROM ft1 WHERE c2 < 0;
RESET constraint_exclusion; RESET constraint_exclusion;
-- check constraint is enforced on the remote side, not locally -- check constraint is enforced on the remote side, not locally
...@@ -792,10 +777,10 @@ ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive; ...@@ -792,10 +777,10 @@ ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive;
-- But inconsistent check constraints provide inconsistent results -- But inconsistent check constraints provide inconsistent results
ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0); ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0);
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 >= 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0;
SELECT count(*) FROM ft1 WHERE c2 >= 0; SELECT count(*) FROM ft1 WHERE c2 >= 0;
SET constraint_exclusion = 'on'; SET constraint_exclusion = 'on';
EXPLAIN (VERBOSE, COSTS false) SELECT count(*) FROM ft1 WHERE c2 >= 0; EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0;
SELECT count(*) FROM ft1 WHERE c2 >= 0; SELECT count(*) FROM ft1 WHERE c2 >= 0;
RESET constraint_exclusion; RESET constraint_exclusion;
-- local check constraint is not actually enforced -- local check constraint is not actually enforced
...@@ -1360,7 +1345,3 @@ WHERE ftrelid = 'table30000'::regclass ...@@ -1360,7 +1345,3 @@ WHERE ftrelid = 'table30000'::regclass
AND ftoptions @> array['fetch_size=60000']; AND ftoptions @> array['fetch_size=60000'];
ROLLBACK; ROLLBACK;
-- Cleanup
DROP OWNED BY view_owner;
DROP USER view_owner;
...@@ -145,10 +145,12 @@ ExecSerializePlan(Plan *plan, EState *estate) ...@@ -145,10 +145,12 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt = makeNode(PlannedStmt); pstmt = makeNode(PlannedStmt);
pstmt->commandType = CMD_SELECT; pstmt->commandType = CMD_SELECT;
pstmt->queryId = 0; pstmt->queryId = 0;
pstmt->hasReturning = 0; pstmt->hasReturning = false;
pstmt->hasModifyingCTE = 0; pstmt->hasModifyingCTE = false;
pstmt->canSetTag = 1; pstmt->canSetTag = true;
pstmt->transientPlan = 0; pstmt->transientPlan = false;
pstmt->dependsOnRole = false;
pstmt->parallelModeNeeded = false;
pstmt->planTree = plan; pstmt->planTree = plan;
pstmt->rtable = estate->es_range_table; pstmt->rtable = estate->es_range_table;
pstmt->resultRelations = NIL; pstmt->resultRelations = NIL;
...@@ -156,11 +158,9 @@ ExecSerializePlan(Plan *plan, EState *estate) ...@@ -156,11 +158,9 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt->subplans = NIL; pstmt->subplans = NIL;
pstmt->rewindPlanIDs = NULL; pstmt->rewindPlanIDs = NULL;
pstmt->rowMarks = NIL; pstmt->rowMarks = NIL;
pstmt->nParamExec = estate->es_plannedstmt->nParamExec;
pstmt->relationOids = NIL; pstmt->relationOids = NIL;
pstmt->invalItems = NIL; /* workers can't replan anyway... */ pstmt->invalItems = NIL; /* workers can't replan anyway... */
pstmt->hasRowSecurity = false; pstmt->nParamExec = estate->es_plannedstmt->nParamExec;
pstmt->hasForeignJoin = false;
/* Return serialized copy of our dummy PlannedStmt. */ /* Return serialized copy of our dummy PlannedStmt. */
return nodeToString(pstmt); return nodeToString(pstmt);
......
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
extern Datum pg_options_to_table(PG_FUNCTION_ARGS); extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS); extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
static HeapTuple find_user_mapping(Oid userid, Oid serverid, bool missing_ok); static HeapTuple find_user_mapping(Oid userid, Oid serverid);
/* /*
* GetForeignDataWrapper - look up the foreign-data wrapper by OID. * GetForeignDataWrapper - look up the foreign-data wrapper by OID.
...@@ -223,7 +224,7 @@ GetUserMapping(Oid userid, Oid serverid) ...@@ -223,7 +224,7 @@ GetUserMapping(Oid userid, Oid serverid)
bool isnull; bool isnull;
UserMapping *um; UserMapping *um;
tp = find_user_mapping(userid, serverid, false); tp = find_user_mapping(userid, serverid);
um = (UserMapping *) palloc(sizeof(UserMapping)); um = (UserMapping *) palloc(sizeof(UserMapping));
um->umid = HeapTupleGetOid(tp); um->umid = HeapTupleGetOid(tp);
...@@ -250,23 +251,14 @@ GetUserMapping(Oid userid, Oid serverid) ...@@ -250,23 +251,14 @@ GetUserMapping(Oid userid, Oid serverid)
* *
* If no mapping is found for the supplied user, we also look for * If no mapping is found for the supplied user, we also look for
* PUBLIC mappings (userid == InvalidOid). * PUBLIC mappings (userid == InvalidOid).
*
* If missing_ok is true, the function returns InvalidOid when it does not find
* required user mapping. Otherwise, find_user_mapping() throws error if it
* does not find required user mapping.
*/ */
Oid Oid
GetUserMappingId(Oid userid, Oid serverid, bool missing_ok) GetUserMappingId(Oid userid, Oid serverid)
{ {
HeapTuple tp; HeapTuple tp;
Oid umid; Oid umid;
tp = find_user_mapping(userid, serverid, missing_ok); tp = find_user_mapping(userid, serverid);
Assert(missing_ok || tp);
if (!tp && missing_ok)
return InvalidOid;
/* Extract the Oid */ /* Extract the Oid */
umid = HeapTupleGetOid(tp); umid = HeapTupleGetOid(tp);
...@@ -276,19 +268,14 @@ GetUserMappingId(Oid userid, Oid serverid, bool missing_ok) ...@@ -276,19 +268,14 @@ GetUserMappingId(Oid userid, Oid serverid, bool missing_ok)
return umid; return umid;
} }
/* /*
* find_user_mapping - Guts of GetUserMapping family. * find_user_mapping - Guts of GetUserMapping family.
* *
* If no mapping is found for the supplied user, we also look for * If no mapping is found for the supplied user, we also look for
* PUBLIC mappings (userid == InvalidOid). * PUBLIC mappings (userid == InvalidOid).
*
* If missing_ok is true, the function returns NULL, if it does not find
* the required user mapping. Otherwise, it throws error if it does not
* find the required user mapping.
*/ */
static HeapTuple static HeapTuple
find_user_mapping(Oid userid, Oid serverid, bool missing_ok) find_user_mapping(Oid userid, Oid serverid)
{ {
HeapTuple tp; HeapTuple tp;
...@@ -305,15 +292,10 @@ find_user_mapping(Oid userid, Oid serverid, bool missing_ok) ...@@ -305,15 +292,10 @@ find_user_mapping(Oid userid, Oid serverid, bool missing_ok)
ObjectIdGetDatum(serverid)); ObjectIdGetDatum(serverid));
if (!HeapTupleIsValid(tp)) if (!HeapTupleIsValid(tp))
{ ereport(ERROR,
if (missing_ok) (errcode(ERRCODE_UNDEFINED_OBJECT),
return NULL; errmsg("user mapping not found for \"%s\"",
else MappingUserName(userid))));
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("user mapping not found for \"%s\"",
MappingUserName(userid))));
}
return tp; return tp;
} }
......
...@@ -85,6 +85,8 @@ _copyPlannedStmt(const PlannedStmt *from) ...@@ -85,6 +85,8 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_SCALAR_FIELD(hasModifyingCTE); COPY_SCALAR_FIELD(hasModifyingCTE);
COPY_SCALAR_FIELD(canSetTag); COPY_SCALAR_FIELD(canSetTag);
COPY_SCALAR_FIELD(transientPlan); COPY_SCALAR_FIELD(transientPlan);
COPY_SCALAR_FIELD(dependsOnRole);
COPY_SCALAR_FIELD(parallelModeNeeded);
COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable); COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(resultRelations);
...@@ -95,9 +97,6 @@ _copyPlannedStmt(const PlannedStmt *from) ...@@ -95,9 +97,6 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(relationOids); COPY_NODE_FIELD(relationOids);
COPY_NODE_FIELD(invalItems); COPY_NODE_FIELD(invalItems);
COPY_SCALAR_FIELD(nParamExec); COPY_SCALAR_FIELD(nParamExec);
COPY_SCALAR_FIELD(hasRowSecurity);
COPY_SCALAR_FIELD(parallelModeNeeded);
COPY_SCALAR_FIELD(hasForeignJoin);
return newnode; return newnode;
} }
......
...@@ -261,6 +261,8 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) ...@@ -261,6 +261,8 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_BOOL_FIELD(hasModifyingCTE); WRITE_BOOL_FIELD(hasModifyingCTE);
WRITE_BOOL_FIELD(canSetTag); WRITE_BOOL_FIELD(canSetTag);
WRITE_BOOL_FIELD(transientPlan); WRITE_BOOL_FIELD(transientPlan);
WRITE_BOOL_FIELD(dependsOnRole);
WRITE_BOOL_FIELD(parallelModeNeeded);
WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable); WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(resultRelations);
...@@ -271,9 +273,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) ...@@ -271,9 +273,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(relationOids); WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems); WRITE_NODE_FIELD(invalItems);
WRITE_INT_FIELD(nParamExec); WRITE_INT_FIELD(nParamExec);
WRITE_BOOL_FIELD(hasRowSecurity);
WRITE_BOOL_FIELD(parallelModeNeeded);
WRITE_BOOL_FIELD(hasForeignJoin);
} }
/* /*
...@@ -2014,11 +2013,11 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) ...@@ -2014,11 +2013,11 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
WRITE_INT_FIELD(nParamExec); WRITE_INT_FIELD(nParamExec);
WRITE_UINT_FIELD(lastPHId); WRITE_UINT_FIELD(lastPHId);
WRITE_UINT_FIELD(lastRowMarkId); WRITE_UINT_FIELD(lastRowMarkId);
WRITE_INT_FIELD(lastPlanNodeId);
WRITE_BOOL_FIELD(transientPlan); WRITE_BOOL_FIELD(transientPlan);
WRITE_BOOL_FIELD(hasRowSecurity); WRITE_BOOL_FIELD(dependsOnRole);
WRITE_BOOL_FIELD(parallelModeOK); WRITE_BOOL_FIELD(parallelModeOK);
WRITE_BOOL_FIELD(parallelModeNeeded); WRITE_BOOL_FIELD(parallelModeNeeded);
WRITE_BOOL_FIELD(hasForeignJoin);
} }
static void static void
...@@ -2106,7 +2105,10 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) ...@@ -2106,7 +2105,10 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_FLOAT_FIELD(allvisfrac, "%.6f"); WRITE_FLOAT_FIELD(allvisfrac, "%.6f");
WRITE_NODE_FIELD(subroot); WRITE_NODE_FIELD(subroot);
WRITE_NODE_FIELD(subplan_params); WRITE_NODE_FIELD(subplan_params);
WRITE_INT_FIELD(rel_parallel_workers);
WRITE_OID_FIELD(serverid); WRITE_OID_FIELD(serverid);
WRITE_OID_FIELD(userid);
WRITE_BOOL_FIELD(useridiscurrent);
/* we don't try to print fdwroutine or fdw_private */ /* we don't try to print fdwroutine or fdw_private */
WRITE_NODE_FIELD(baserestrictinfo); WRITE_NODE_FIELD(baserestrictinfo);
WRITE_NODE_FIELD(joininfo); WRITE_NODE_FIELD(joininfo);
......
...@@ -1390,6 +1390,8 @@ _readPlannedStmt(void) ...@@ -1390,6 +1390,8 @@ _readPlannedStmt(void)
READ_BOOL_FIELD(hasModifyingCTE); READ_BOOL_FIELD(hasModifyingCTE);
READ_BOOL_FIELD(canSetTag); READ_BOOL_FIELD(canSetTag);
READ_BOOL_FIELD(transientPlan); READ_BOOL_FIELD(transientPlan);
READ_BOOL_FIELD(dependsOnRole);
READ_BOOL_FIELD(parallelModeNeeded);
READ_NODE_FIELD(planTree); READ_NODE_FIELD(planTree);
READ_NODE_FIELD(rtable); READ_NODE_FIELD(rtable);
READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(resultRelations);
...@@ -1400,9 +1402,6 @@ _readPlannedStmt(void) ...@@ -1400,9 +1402,6 @@ _readPlannedStmt(void)
READ_NODE_FIELD(relationOids); READ_NODE_FIELD(relationOids);
READ_NODE_FIELD(invalItems); READ_NODE_FIELD(invalItems);
READ_INT_FIELD(nParamExec); READ_INT_FIELD(nParamExec);
READ_BOOL_FIELD(hasRowSecurity);
READ_BOOL_FIELD(parallelModeNeeded);
READ_BOOL_FIELD(hasForeignJoin);
READ_DONE(); READ_DONE();
} }
......
...@@ -213,8 +213,8 @@ add_paths_to_joinrel(PlannerInfo *root, ...@@ -213,8 +213,8 @@ add_paths_to_joinrel(PlannerInfo *root,
/* /*
* 5. If inner and outer relations are foreign tables (or joins) belonging * 5. If inner and outer relations are foreign tables (or joins) belonging
* to the same server and using the same user mapping, give the FDW a * to the same server and assigned to the same user to check access
* chance to push down joins. * permissions as, give the FDW a chance to push down joins.
*/ */
if (joinrel->fdwroutine && if (joinrel->fdwroutine &&
joinrel->fdwroutine->GetForeignJoinPaths) joinrel->fdwroutine->GetForeignJoinPaths)
......
...@@ -3247,13 +3247,12 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, ...@@ -3247,13 +3247,12 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
scan_plan->fs_relids = best_path->path.parent->relids; scan_plan->fs_relids = best_path->path.parent->relids;
/* /*
* If a join between foreign relations was pushed down, remember it. The * If this is a foreign join, and to make it valid to push down we had to
* push-down safety of the join depends upon the server and user mapping * assume that the current user is the same as some user explicitly named
* being same. That can change between planning and execution time, in * in the query, mark the finished plan as depending on the current user.
* which case the plan should be invalidated.
*/ */
if (scan_relid == 0) if (rel->useridiscurrent)
root->glob->hasForeignJoin = true; root->glob->dependsOnRole = true;
/* /*
* Replace any outer-relation variables with nestloop params in the qual, * Replace any outer-relation variables with nestloop params in the qual,
......
...@@ -219,8 +219,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) ...@@ -219,8 +219,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->lastRowMarkId = 0; glob->lastRowMarkId = 0;
glob->lastPlanNodeId = 0; glob->lastPlanNodeId = 0;
glob->transientPlan = false; glob->transientPlan = false;
glob->hasRowSecurity = false; glob->dependsOnRole = false;
glob->hasForeignJoin = false;
/* /*
* Assess whether it's feasible to use parallel mode for this query. We * Assess whether it's feasible to use parallel mode for this query. We
...@@ -405,6 +404,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) ...@@ -405,6 +404,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->hasModifyingCTE = parse->hasModifyingCTE; result->hasModifyingCTE = parse->hasModifyingCTE;
result->canSetTag = parse->canSetTag; result->canSetTag = parse->canSetTag;
result->transientPlan = glob->transientPlan; result->transientPlan = glob->transientPlan;
result->dependsOnRole = glob->dependsOnRole;
result->parallelModeNeeded = glob->parallelModeNeeded;
result->planTree = top_plan; result->planTree = top_plan;
result->rtable = glob->finalrtable; result->rtable = glob->finalrtable;
result->resultRelations = glob->resultRelations; result->resultRelations = glob->resultRelations;
...@@ -415,9 +416,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) ...@@ -415,9 +416,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->relationOids = glob->relationOids; result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems; result->invalItems = glob->invalItems;
result->nParamExec = glob->nParamExec; result->nParamExec = glob->nParamExec;
result->hasRowSecurity = glob->hasRowSecurity;
result->parallelModeNeeded = glob->parallelModeNeeded;
result->hasForeignJoin = glob->hasForeignJoin;
return result; return result;
} }
...@@ -1628,8 +1626,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, ...@@ -1628,8 +1626,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* This may add new security barrier subquery RTEs to the rangetable. * This may add new security barrier subquery RTEs to the rangetable.
*/ */
expand_security_quals(root, tlist); expand_security_quals(root, tlist);
if (parse->hasRowSecurity)
root->glob->hasRowSecurity = true;
/* /*
* We are now done hacking up the query's targetlist. Most of the * We are now done hacking up the query's targetlist. Most of the
...@@ -1960,7 +1956,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, ...@@ -1960,7 +1956,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
* If the current_rel belongs to a single FDW, so does the final_rel. * If the current_rel belongs to a single FDW, so does the final_rel.
*/ */
final_rel->serverid = current_rel->serverid; final_rel->serverid = current_rel->serverid;
final_rel->umid = current_rel->umid; final_rel->userid = current_rel->userid;
final_rel->useridiscurrent = current_rel->useridiscurrent;
final_rel->fdwroutine = current_rel->fdwroutine; final_rel->fdwroutine = current_rel->fdwroutine;
/* /*
...@@ -3337,7 +3334,8 @@ create_grouping_paths(PlannerInfo *root, ...@@ -3337,7 +3334,8 @@ create_grouping_paths(PlannerInfo *root,
* If the input rel belongs to a single FDW, so does the grouped rel. * If the input rel belongs to a single FDW, so does the grouped rel.
*/ */
grouped_rel->serverid = input_rel->serverid; grouped_rel->serverid = input_rel->serverid;
grouped_rel->umid = input_rel->umid; grouped_rel->userid = input_rel->userid;
grouped_rel->useridiscurrent = input_rel->useridiscurrent;
grouped_rel->fdwroutine = input_rel->fdwroutine; grouped_rel->fdwroutine = input_rel->fdwroutine;
/* /*
...@@ -3891,7 +3889,8 @@ create_window_paths(PlannerInfo *root, ...@@ -3891,7 +3889,8 @@ create_window_paths(PlannerInfo *root,
* If the input rel belongs to a single FDW, so does the window rel. * If the input rel belongs to a single FDW, so does the window rel.
*/ */
window_rel->serverid = input_rel->serverid; window_rel->serverid = input_rel->serverid;
window_rel->umid = input_rel->umid; window_rel->userid = input_rel->userid;
window_rel->useridiscurrent = input_rel->useridiscurrent;
window_rel->fdwroutine = input_rel->fdwroutine; window_rel->fdwroutine = input_rel->fdwroutine;
/* /*
...@@ -4071,7 +4070,8 @@ create_distinct_paths(PlannerInfo *root, ...@@ -4071,7 +4070,8 @@ create_distinct_paths(PlannerInfo *root,
* If the input rel belongs to a single FDW, so does the distinct_rel. * If the input rel belongs to a single FDW, so does the distinct_rel.
*/ */
distinct_rel->serverid = input_rel->serverid; distinct_rel->serverid = input_rel->serverid;
distinct_rel->umid = input_rel->umid; distinct_rel->userid = input_rel->userid;
distinct_rel->useridiscurrent = input_rel->useridiscurrent;
distinct_rel->fdwroutine = input_rel->fdwroutine; distinct_rel->fdwroutine = input_rel->fdwroutine;
/* Estimate number of distinct rows there will be */ /* Estimate number of distinct rows there will be */
...@@ -4279,7 +4279,8 @@ create_ordered_paths(PlannerInfo *root, ...@@ -4279,7 +4279,8 @@ create_ordered_paths(PlannerInfo *root,
* If the input rel belongs to a single FDW, so does the ordered_rel. * If the input rel belongs to a single FDW, so does the ordered_rel.
*/ */
ordered_rel->serverid = input_rel->serverid; ordered_rel->serverid = input_rel->serverid;
ordered_rel->umid = input_rel->umid; ordered_rel->userid = input_rel->userid;
ordered_rel->useridiscurrent = input_rel->useridiscurrent;
ordered_rel->fdwroutine = input_rel->fdwroutine; ordered_rel->fdwroutine = input_rel->fdwroutine;
foreach(lc, input_rel->pathlist) foreach(lc, input_rel->pathlist)
......
...@@ -2432,9 +2432,10 @@ record_plan_function_dependency(PlannerInfo *root, Oid funcid) ...@@ -2432,9 +2432,10 @@ record_plan_function_dependency(PlannerInfo *root, Oid funcid)
/* /*
* extract_query_dependencies * extract_query_dependencies
* Given a not-yet-planned query or queries (i.e. a Query node or list * Given a rewritten, but not yet planned, query or queries
* of Query nodes), extract dependencies just as set_plan_references * (i.e. a Query node or list of Query nodes), extract dependencies
* would do. * just as set_plan_references would do. Also detect whether any
* rewrite steps were affected by RLS.
* *
* This is needed by plancache.c to handle invalidation of cached unplanned * This is needed by plancache.c to handle invalidation of cached unplanned
* queries. * queries.
...@@ -2453,7 +2454,8 @@ extract_query_dependencies(Node *query, ...@@ -2453,7 +2454,8 @@ extract_query_dependencies(Node *query,
glob.type = T_PlannerGlobal; glob.type = T_PlannerGlobal;
glob.relationOids = NIL; glob.relationOids = NIL;
glob.invalItems = NIL; glob.invalItems = NIL;
glob.hasRowSecurity = false; /* Hack: we use glob.dependsOnRole to collect hasRowSecurity flags */
glob.dependsOnRole = false;
MemSet(&root, 0, sizeof(root)); MemSet(&root, 0, sizeof(root));
root.type = T_PlannerInfo; root.type = T_PlannerInfo;
...@@ -2463,7 +2465,7 @@ extract_query_dependencies(Node *query, ...@@ -2463,7 +2465,7 @@ extract_query_dependencies(Node *query,
*relationOids = glob.relationOids; *relationOids = glob.relationOids;
*invalItems = glob.invalItems; *invalItems = glob.invalItems;
*hasRowSecurity = glob.hasRowSecurity; *hasRowSecurity = glob.dependsOnRole;
} }
static bool static bool
...@@ -2479,10 +2481,6 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) ...@@ -2479,10 +2481,6 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
Query *query = (Query *) node; Query *query = (Query *) node;
ListCell *lc; ListCell *lc;
/* Collect row security information */
if (query->hasRowSecurity)
context->glob->hasRowSecurity = true;
if (query->commandType == CMD_UTILITY) if (query->commandType == CMD_UTILITY)
{ {
/* /*
...@@ -2494,6 +2492,10 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) ...@@ -2494,6 +2492,10 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
return false; return false;
} }
/* Remember if any Query has RLS quals applied by rewriter */
if (query->hasRowSecurity)
context->glob->dependsOnRole = true;
/* Collect relation OIDs in this Query's rtable */ /* Collect relation OIDs in this Query's rtable */
foreach(lc, query->rtable) foreach(lc, query->rtable)
{ {
......
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#include "postgres.h" #include "postgres.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "catalog/pg_class.h"
#include "foreign/foreign.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
...@@ -107,7 +105,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) ...@@ -107,7 +105,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->consider_startup = (root->tuple_fraction > 0); rel->consider_startup = (root->tuple_fraction > 0);
rel->consider_param_startup = false; /* might get changed later */ rel->consider_param_startup = false; /* might get changed later */
rel->consider_parallel = false; /* might get changed later */ rel->consider_parallel = false; /* might get changed later */
rel->rel_parallel_workers = -1; /* set up in GetRelationInfo */
rel->reltarget = create_empty_pathtarget(); rel->reltarget = create_empty_pathtarget();
rel->pathlist = NIL; rel->pathlist = NIL;
rel->ppilist = NIL; rel->ppilist = NIL;
...@@ -129,8 +126,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) ...@@ -129,8 +126,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->allvisfrac = 0; rel->allvisfrac = 0;
rel->subroot = NULL; rel->subroot = NULL;
rel->subplan_params = NIL; rel->subplan_params = NIL;
rel->rel_parallel_workers = -1; /* set up in GetRelationInfo */
rel->serverid = InvalidOid; rel->serverid = InvalidOid;
rel->umid = InvalidOid; rel->userid = rte->checkAsUser;
rel->useridiscurrent = false;
rel->fdwroutine = NULL; rel->fdwroutine = NULL;
rel->fdw_private = NULL; rel->fdw_private = NULL;
rel->baserestrictinfo = NIL; rel->baserestrictinfo = NIL;
...@@ -170,30 +169,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) ...@@ -170,30 +169,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
break; break;
} }
/* For foreign tables get the user mapping */
if (rte->relkind == RELKIND_FOREIGN_TABLE)
{
/*
* This should match what ExecCheckRTEPerms() does.
*
* Note that if the plan ends up depending on the user OID in any way
* - e.g. if it depends on the computed user mapping OID - we must
* ensure that it gets invalidated in the case of a user OID change.
* See RevalidateCachedQuery and more generally the hasForeignJoin
* flags in PlannerGlobal and PlannedStmt.
*
* It's possible, and not necessarily an error, for rel->umid to be
* InvalidOid even though rel->serverid is set. That just means there
* is a server with no user mapping.
*/
Oid userid;
userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId();
rel->umid = GetUserMappingId(userid, rel->serverid, true);
}
else
rel->umid = InvalidOid;
/* Save the finished struct in the query's simple_rel_array */ /* Save the finished struct in the query's simple_rel_array */
root->simple_rel_array[relid] = rel; root->simple_rel_array[relid] = rel;
...@@ -423,8 +398,10 @@ build_join_rel(PlannerInfo *root, ...@@ -423,8 +398,10 @@ build_join_rel(PlannerInfo *root,
joinrel->allvisfrac = 0; joinrel->allvisfrac = 0;
joinrel->subroot = NULL; joinrel->subroot = NULL;
joinrel->subplan_params = NIL; joinrel->subplan_params = NIL;
joinrel->rel_parallel_workers = -1;
joinrel->serverid = InvalidOid; joinrel->serverid = InvalidOid;
joinrel->umid = InvalidOid; joinrel->userid = InvalidOid;
joinrel->useridiscurrent = false;
joinrel->fdwroutine = NULL; joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL; joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL; joinrel->baserestrictinfo = NIL;
...@@ -435,24 +412,43 @@ build_join_rel(PlannerInfo *root, ...@@ -435,24 +412,43 @@ build_join_rel(PlannerInfo *root,
/* /*
* Set up foreign-join fields if outer and inner relation are foreign * Set up foreign-join fields if outer and inner relation are foreign
* tables (or joins) belonging to the same server and using the same user * tables (or joins) belonging to the same server and assigned to the same
* mapping. * user to check access permissions as. In addition to an exact match of
* * userid, we allow the case where one side has zero userid (implying
* Otherwise those fields are left invalid, so FDW API will not be called * current user) and the other side has explicit userid that happens to
* for the join relation. * equal the current user; but in that case, pushdown of the join is only
* valid for the current user. The useridiscurrent field records whether
* we had to make such an assumption for this join or any sub-join.
* *
* For FDWs like file_fdw, which ignore user mapping, the user mapping id * Otherwise these fields are left invalid, so GetForeignJoinPaths will
* associated with the joining relation may be invalid. A valid serverid * not be called for the join relation.
* distinguishes between a pushed down join with no user mapping and a
* join which can not be pushed down because of user mapping mismatch.
*/ */
if (OidIsValid(outer_rel->serverid) && if (OidIsValid(outer_rel->serverid) &&
inner_rel->serverid == outer_rel->serverid && inner_rel->serverid == outer_rel->serverid)
inner_rel->umid == outer_rel->umid)
{ {
joinrel->serverid = outer_rel->serverid; if (inner_rel->userid == outer_rel->userid)
joinrel->umid = outer_rel->umid; {
joinrel->fdwroutine = outer_rel->fdwroutine; joinrel->serverid = outer_rel->serverid;
joinrel->userid = outer_rel->userid;
joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent;
joinrel->fdwroutine = outer_rel->fdwroutine;
}
else if (!OidIsValid(inner_rel->userid) &&
outer_rel->userid == GetUserId())
{
joinrel->serverid = outer_rel->serverid;
joinrel->userid = outer_rel->userid;
joinrel->useridiscurrent = true;
joinrel->fdwroutine = outer_rel->fdwroutine;
}
else if (!OidIsValid(outer_rel->userid) &&
inner_rel->userid == GetUserId())
{
joinrel->serverid = outer_rel->serverid;
joinrel->userid = inner_rel->userid;
joinrel->useridiscurrent = true;
joinrel->fdwroutine = outer_rel->fdwroutine;
}
} }
/* /*
......
...@@ -100,13 +100,10 @@ static void AcquireExecutorLocks(List *stmt_list, bool acquire); ...@@ -100,13 +100,10 @@ static void AcquireExecutorLocks(List *stmt_list, bool acquire);
static void AcquirePlannerLocks(List *stmt_list, bool acquire); static void AcquirePlannerLocks(List *stmt_list, bool acquire);
static void ScanQueryForLocks(Query *parsetree, bool acquire); static void ScanQueryForLocks(Query *parsetree, bool acquire);
static bool ScanQueryWalker(Node *node, bool *acquire); static bool ScanQueryWalker(Node *node, bool *acquire);
static bool plan_list_is_transient(List *stmt_list);
static TupleDesc PlanCacheComputeResultDesc(List *stmt_list); static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
static void PlanCacheRelCallback(Datum arg, Oid relid); static void PlanCacheRelCallback(Datum arg, Oid relid);
static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue); static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue);
static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue); static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
static void PlanCacheUserMappingCallback(Datum arg, int cacheid,
uint32 hashvalue);
/* /*
...@@ -122,8 +119,6 @@ InitPlanCache(void) ...@@ -122,8 +119,6 @@ InitPlanCache(void)
CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0); CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0); CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0); CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
/* User mapping change may invalidate plans with pushed down foreign join */
CacheRegisterSyscacheCallback(USERMAPPINGOID, PlanCacheUserMappingCallback, (Datum) 0);
} }
/* /*
...@@ -198,6 +193,9 @@ CreateCachedPlan(Node *raw_parse_tree, ...@@ -198,6 +193,9 @@ CreateCachedPlan(Node *raw_parse_tree,
plansource->invalItems = NIL; plansource->invalItems = NIL;
plansource->search_path = NULL; plansource->search_path = NULL;
plansource->query_context = NULL; plansource->query_context = NULL;
plansource->rewriteRoleId = InvalidOid;
plansource->rewriteRowSecurity = false;
plansource->dependsOnRLS = false;
plansource->gplan = NULL; plansource->gplan = NULL;
plansource->is_oneshot = false; plansource->is_oneshot = false;
plansource->is_complete = false; plansource->is_complete = false;
...@@ -208,9 +206,6 @@ CreateCachedPlan(Node *raw_parse_tree, ...@@ -208,9 +206,6 @@ CreateCachedPlan(Node *raw_parse_tree,
plansource->generic_cost = -1; plansource->generic_cost = -1;
plansource->total_custom_cost = 0; plansource->total_custom_cost = 0;
plansource->num_custom_plans = 0; plansource->num_custom_plans = 0;
plansource->hasRowSecurity = false;
plansource->planUserId = InvalidOid;
plansource->row_security_env = false;
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
...@@ -266,6 +261,9 @@ CreateOneShotCachedPlan(Node *raw_parse_tree, ...@@ -266,6 +261,9 @@ CreateOneShotCachedPlan(Node *raw_parse_tree,
plansource->invalItems = NIL; plansource->invalItems = NIL;
plansource->search_path = NULL; plansource->search_path = NULL;
plansource->query_context = NULL; plansource->query_context = NULL;
plansource->rewriteRoleId = InvalidOid;
plansource->rewriteRowSecurity = false;
plansource->dependsOnRLS = false;
plansource->gplan = NULL; plansource->gplan = NULL;
plansource->is_oneshot = true; plansource->is_oneshot = true;
plansource->is_complete = false; plansource->is_complete = false;
...@@ -276,8 +274,6 @@ CreateOneShotCachedPlan(Node *raw_parse_tree, ...@@ -276,8 +274,6 @@ CreateOneShotCachedPlan(Node *raw_parse_tree,
plansource->generic_cost = -1; plansource->generic_cost = -1;
plansource->total_custom_cost = 0; plansource->total_custom_cost = 0;
plansource->num_custom_plans = 0; plansource->num_custom_plans = 0;
plansource->planUserId = InvalidOid;
plansource->row_security_env = false;
return plansource; return plansource;
} }
...@@ -384,7 +380,11 @@ CompleteCachedPlan(CachedPlanSource *plansource, ...@@ -384,7 +380,11 @@ CompleteCachedPlan(CachedPlanSource *plansource,
extract_query_dependencies((Node *) querytree_list, extract_query_dependencies((Node *) querytree_list,
&plansource->relationOids, &plansource->relationOids,
&plansource->invalItems, &plansource->invalItems,
&plansource->hasRowSecurity); &plansource->dependsOnRLS);
/* Update RLS info as well. */
plansource->rewriteRoleId = GetUserId();
plansource->rewriteRowSecurity = row_security;
/* /*
* Also save the current search_path in the query_context. (This * Also save the current search_path in the query_context. (This
...@@ -416,8 +416,6 @@ CompleteCachedPlan(CachedPlanSource *plansource, ...@@ -416,8 +416,6 @@ CompleteCachedPlan(CachedPlanSource *plansource,
plansource->cursor_options = cursor_options; plansource->cursor_options = cursor_options;
plansource->fixed_result = fixed_result; plansource->fixed_result = fixed_result;
plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list); plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list);
plansource->planUserId = GetUserId();
plansource->row_security_env = row_security;
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
...@@ -583,12 +581,10 @@ RevalidateCachedQuery(CachedPlanSource *plansource) ...@@ -583,12 +581,10 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
/* /*
* If the query is currently valid, we should have a saved search_path --- * If the query is currently valid, we should have a saved search_path ---
* check to see if that matches the current environment. If not, we want * check to see if that matches the current environment. If not, we want
* to force replan. We should also have a valid planUserId. * to force replan.
*/ */
if (plansource->is_valid) if (plansource->is_valid)
{ {
Assert(OidIsValid(plansource->planUserId));
Assert(plansource->search_path != NULL); Assert(plansource->search_path != NULL);
if (!OverrideSearchPathMatchesCurrent(plansource->search_path)) if (!OverrideSearchPathMatchesCurrent(plansource->search_path))
{ {
...@@ -600,28 +596,14 @@ RevalidateCachedQuery(CachedPlanSource *plansource) ...@@ -600,28 +596,14 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
} }
/* /*
* If the plan has a possible RLS dependency, force a replan if either the * If the query rewrite phase had a possible RLS dependency, we must redo
* role or the row_security setting has changed. * it if either the role or the row_security setting has changed.
*/ */
if (plansource->is_valid if (plansource->is_valid && plansource->dependsOnRLS &&
&& plansource->hasRowSecurity (plansource->rewriteRoleId != GetUserId() ||
&& (plansource->planUserId != GetUserId() plansource->rewriteRowSecurity != row_security))
|| plansource->row_security_env != row_security))
plansource->is_valid = false; plansource->is_valid = false;
/*
* If we have a join pushed down to the foreign server and the current
* user is different from the one for which the plan was created,
* invalidate the generic plan since user mapping for the new user might
* make the join unsafe to push down, or change which user mapping is
* used.
*/
if (plansource->is_valid &&
plansource->gplan &&
plansource->gplan->has_foreign_join &&
plansource->planUserId != GetUserId())
plansource->gplan->is_valid = false;
/* /*
* If the query is currently valid, acquire locks on the referenced * If the query is currently valid, acquire locks on the referenced
* objects; then check again. We need to do it this way to cover the race * objects; then check again. We need to do it this way to cover the race
...@@ -656,14 +638,6 @@ RevalidateCachedQuery(CachedPlanSource *plansource) ...@@ -656,14 +638,6 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
plansource->invalItems = NIL; plansource->invalItems = NIL;
plansource->search_path = NULL; plansource->search_path = NULL;
/*
* The plan is invalid, possibly due to row security, so we need to reset
* row_security_env and planUserId as we're about to re-plan with the
* current settings.
*/
plansource->row_security_env = row_security;
plansource->planUserId = GetUserId();
/* /*
* Free the query_context. We don't really expect MemoryContextDelete to * Free the query_context. We don't really expect MemoryContextDelete to
* fail, but just in case, make sure the CachedPlanSource is left in a * fail, but just in case, make sure the CachedPlanSource is left in a
...@@ -774,7 +748,11 @@ RevalidateCachedQuery(CachedPlanSource *plansource) ...@@ -774,7 +748,11 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
extract_query_dependencies((Node *) qlist, extract_query_dependencies((Node *) qlist,
&plansource->relationOids, &plansource->relationOids,
&plansource->invalItems, &plansource->invalItems,
&plansource->hasRowSecurity); &plansource->dependsOnRLS);
/* Update RLS info as well. */
plansource->rewriteRoleId = GetUserId();
plansource->rewriteRowSecurity = row_security;
/* /*
* Also save the current search_path in the query_context. (This should * Also save the current search_path in the query_context. (This should
...@@ -831,6 +809,13 @@ CheckCachedPlan(CachedPlanSource *plansource) ...@@ -831,6 +809,13 @@ CheckCachedPlan(CachedPlanSource *plansource)
/* Generic plans are never one-shot */ /* Generic plans are never one-shot */
Assert(!plan->is_oneshot); Assert(!plan->is_oneshot);
/*
* If plan isn't valid for current role, we can't use it.
*/
if (plan->is_valid && plan->dependsOnRole &&
plan->planRoleId != GetUserId())
plan->is_valid = false;
/* /*
* If it appears valid, acquire locks and recheck; this is much the same * If it appears valid, acquire locks and recheck; this is much the same
* logic as in RevalidateCachedQuery, but for a plan. * logic as in RevalidateCachedQuery, but for a plan.
...@@ -900,6 +885,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, ...@@ -900,6 +885,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
List *plist; List *plist;
bool snapshot_set; bool snapshot_set;
bool spi_pushed; bool spi_pushed;
bool is_transient;
MemoryContext plan_context; MemoryContext plan_context;
MemoryContext oldcxt = CurrentMemoryContext; MemoryContext oldcxt = CurrentMemoryContext;
ListCell *lc; ListCell *lc;
...@@ -997,7 +983,28 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, ...@@ -997,7 +983,28 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
plan = (CachedPlan *) palloc(sizeof(CachedPlan)); plan = (CachedPlan *) palloc(sizeof(CachedPlan));
plan->magic = CACHEDPLAN_MAGIC; plan->magic = CACHEDPLAN_MAGIC;
plan->stmt_list = plist; plan->stmt_list = plist;
if (plan_list_is_transient(plist))
/*
* CachedPlan is dependent on role either if RLS affected the rewrite
* phase or if a role dependency was injected during planning. And it's
* transient if any plan is marked so.
*/
plan->planRoleId = GetUserId();
plan->dependsOnRole = plansource->dependsOnRLS;
is_transient = false;
foreach(lc, plist)
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
if (!IsA(plannedstmt, PlannedStmt))
continue; /* Ignore utility statements */
if (plannedstmt->transientPlan)
is_transient = true;
if (plannedstmt->dependsOnRole)
plan->dependsOnRole = true;
}
if (is_transient)
{ {
Assert(TransactionIdIsNormal(TransactionXmin)); Assert(TransactionIdIsNormal(TransactionXmin));
plan->saved_xmin = TransactionXmin; plan->saved_xmin = TransactionXmin;
...@@ -1010,20 +1017,6 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, ...@@ -1010,20 +1017,6 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
plan->is_saved = false; plan->is_saved = false;
plan->is_valid = true; plan->is_valid = true;
/*
* Walk through the plist and set hasForeignJoin if any of the plans have
* it set.
*/
plan->has_foreign_join = false;
foreach(lc, plist)
{
PlannedStmt *plan_stmt = (PlannedStmt *) lfirst(lc);
if (IsA(plan_stmt, PlannedStmt))
plan->has_foreign_join =
plan->has_foreign_join || plan_stmt->hasForeignJoin;
}
/* assign generation number to new plan */ /* assign generation number to new plan */
plan->generation = ++(plansource->generation); plan->generation = ++(plansource->generation);
...@@ -1401,6 +1394,9 @@ CopyCachedPlan(CachedPlanSource *plansource) ...@@ -1401,6 +1394,9 @@ CopyCachedPlan(CachedPlanSource *plansource)
if (plansource->search_path) if (plansource->search_path)
newsource->search_path = CopyOverrideSearchPath(plansource->search_path); newsource->search_path = CopyOverrideSearchPath(plansource->search_path);
newsource->query_context = querytree_context; newsource->query_context = querytree_context;
newsource->rewriteRoleId = plansource->rewriteRoleId;
newsource->rewriteRowSecurity = plansource->rewriteRowSecurity;
newsource->dependsOnRLS = plansource->dependsOnRLS;
newsource->gplan = NULL; newsource->gplan = NULL;
...@@ -1416,14 +1412,6 @@ CopyCachedPlan(CachedPlanSource *plansource) ...@@ -1416,14 +1412,6 @@ CopyCachedPlan(CachedPlanSource *plansource)
newsource->total_custom_cost = plansource->total_custom_cost; newsource->total_custom_cost = plansource->total_custom_cost;
newsource->num_custom_plans = plansource->num_custom_plans; newsource->num_custom_plans = plansource->num_custom_plans;
/*
* Copy over the user the query was planned as, and under what RLS
* environment. We will check during RevalidateCachedQuery() if the user
* or environment has changed and, if so, will force a re-plan.
*/
newsource->planUserId = plansource->planUserId;
newsource->row_security_env = plansource->row_security_env;
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
return newsource; return newsource;
...@@ -1667,28 +1655,6 @@ ScanQueryWalker(Node *node, bool *acquire) ...@@ -1667,28 +1655,6 @@ ScanQueryWalker(Node *node, bool *acquire)
(void *) acquire); (void *) acquire);
} }
/*
* plan_list_is_transient: check if any of the plans in the list are transient.
*/
static bool
plan_list_is_transient(List *stmt_list)
{
ListCell *lc;
foreach(lc, stmt_list)
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
if (!IsA(plannedstmt, PlannedStmt))
continue; /* Ignore utility statements */
if (plannedstmt->transientPlan)
return true;
}
return false;
}
/* /*
* PlanCacheComputeResultDesc: given a list of analyzed-and-rewritten Queries, * PlanCacheComputeResultDesc: given a list of analyzed-and-rewritten Queries,
* determine the result tupledesc it will produce. Returns NULL if the * determine the result tupledesc it will produce. Returns NULL if the
...@@ -1887,40 +1853,6 @@ PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue) ...@@ -1887,40 +1853,6 @@ PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
ResetPlanCache(); ResetPlanCache();
} }
/*
* PlanCacheUserMappingCallback
* Syscache inval callback function for user mapping cache invalidation.
*
* Invalidates plans which have pushed down foreign joins.
*/
static void
PlanCacheUserMappingCallback(Datum arg, int cacheid, uint32 hashvalue)
{
CachedPlanSource *plansource;
for (plansource = first_saved_plan; plansource; plansource = plansource->next_saved)
{
Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
/* No work if it's already invalidated */
if (!plansource->is_valid)
continue;
/* Never invalidate transaction control commands */
if (IsTransactionStmtPlan(plansource))
continue;
/*
* If the plan has pushed down foreign joins, those join may become
* unsafe to push down because of user mapping changes. Invalidate
* only the generic plan, since changes to user mapping do not
* invalidate the parse tree.
*/
if (plansource->gplan && plansource->gplan->has_foreign_join)
plansource->gplan->is_valid = false;
}
}
/* /*
* ResetPlanCache: invalidate all cached plans. * ResetPlanCache: invalidate all cached plans.
*/ */
......
...@@ -72,7 +72,7 @@ typedef struct ForeignTable ...@@ -72,7 +72,7 @@ typedef struct ForeignTable
extern ForeignServer *GetForeignServer(Oid serverid); extern ForeignServer *GetForeignServer(Oid serverid);
extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok); extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
extern UserMapping *GetUserMapping(Oid userid, Oid serverid); extern UserMapping *GetUserMapping(Oid userid, Oid serverid);
extern Oid GetUserMappingId(Oid userid, Oid serverid, bool missing_ok); extern Oid GetUserMappingId(Oid userid, Oid serverid);
extern UserMapping *GetUserMappingById(Oid umid); extern UserMapping *GetUserMappingById(Oid umid);
extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid); extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name, extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
......
...@@ -121,7 +121,7 @@ typedef struct Query ...@@ -121,7 +121,7 @@ typedef struct Query
bool hasRecursive; /* WITH RECURSIVE was specified */ bool hasRecursive; /* WITH RECURSIVE was specified */
bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */ bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */ bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
bool hasRowSecurity; /* row security applied? */ bool hasRowSecurity; /* rewriter has applied some RLS policy */
List *cteList; /* WITH list (of CommonTableExpr's) */ List *cteList; /* WITH list (of CommonTableExpr's) */
...@@ -2823,7 +2823,7 @@ typedef enum VacuumOption ...@@ -2823,7 +2823,7 @@ typedef enum VacuumOption
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */ VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
} VacuumOption; } VacuumOption;
typedef struct VacuumStmt typedef struct VacuumStmt
......
...@@ -49,6 +49,10 @@ typedef struct PlannedStmt ...@@ -49,6 +49,10 @@ typedef struct PlannedStmt
bool transientPlan; /* redo plan when TransactionXmin changes? */ bool transientPlan; /* redo plan when TransactionXmin changes? */
bool dependsOnRole; /* is plan specific to current role? */
bool parallelModeNeeded; /* parallel mode required to execute? */
struct Plan *planTree; /* tree of Plan nodes */ struct Plan *planTree; /* tree of Plan nodes */
List *rtable; /* list of RangeTblEntry nodes */ List *rtable; /* list of RangeTblEntry nodes */
...@@ -69,11 +73,6 @@ typedef struct PlannedStmt ...@@ -69,11 +73,6 @@ typedef struct PlannedStmt
List *invalItems; /* other dependencies, as PlanInvalItems */ List *invalItems; /* other dependencies, as PlanInvalItems */
int nParamExec; /* number of PARAM_EXEC Params used */ int nParamExec; /* number of PARAM_EXEC Params used */
bool hasRowSecurity; /* row security applied? */
bool parallelModeNeeded; /* parallel mode required to execute? */
bool hasForeignJoin; /* Plan has a pushed down foreign join */
} PlannedStmt; } PlannedStmt;
/* macro for fetching the Plan associated with a SubPlan node */ /* macro for fetching the Plan associated with a SubPlan node */
......
...@@ -121,13 +121,11 @@ typedef struct PlannerGlobal ...@@ -121,13 +121,11 @@ typedef struct PlannerGlobal
bool transientPlan; /* redo plan when TransactionXmin changes? */ bool transientPlan; /* redo plan when TransactionXmin changes? */
bool hasRowSecurity; /* row security applied? */ bool dependsOnRole; /* is plan specific to current role? */
bool parallelModeOK; /* parallel mode potentially OK? */ bool parallelModeOK; /* parallel mode potentially OK? */
bool parallelModeNeeded; /* parallel mode actually required? */ bool parallelModeNeeded; /* parallel mode actually required? */
bool hasForeignJoin; /* does have a pushed down foreign join */
} PlannerGlobal; } PlannerGlobal;
/* macro for fetching the Plan associated with a SubPlan node */ /* macro for fetching the Plan associated with a SubPlan node */
...@@ -426,11 +424,12 @@ typedef struct PlannerInfo ...@@ -426,11 +424,12 @@ typedef struct PlannerInfo
* in just as for a baserel, except we don't bother with lateral_vars. * in just as for a baserel, except we don't bother with lateral_vars.
* *
* If the relation is either a foreign table or a join of foreign tables that * If the relation is either a foreign table or a join of foreign tables that
* all belong to the same foreign server and use the same user mapping, these * all belong to the same foreign server and are assigned to the same user to
* fields will be set: * check access permissions as (cf checkAsUser), these fields will be set:
* *
* serverid - OID of foreign server, if foreign table (else InvalidOid) * serverid - OID of foreign server, if foreign table (else InvalidOid)
* umid - OID of user mapping, if foreign table (else InvalidOid) * userid - OID of user to check access as (InvalidOid means current user)
* useridiscurrent - we've assumed that userid equals current user
* fdwroutine - function hooks for FDW, if foreign table (else NULL) * fdwroutine - function hooks for FDW, if foreign table (else NULL)
* fdw_private - private state for FDW, if foreign table (else NULL) * fdw_private - private state for FDW, if foreign table (else NULL)
* *
...@@ -528,8 +527,8 @@ typedef struct RelOptInfo ...@@ -528,8 +527,8 @@ typedef struct RelOptInfo
/* Information about foreign tables and foreign joins */ /* Information about foreign tables and foreign joins */
Oid serverid; /* identifies server for the table or join */ Oid serverid; /* identifies server for the table or join */
Oid umid; /* identifies user mapping for the table or Oid userid; /* identifies user to check access as */
* join */ bool useridiscurrent; /* join is only valid for current user */
/* use "struct FdwRoutine" to avoid including fdwapi.h here */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */
struct FdwRoutine *fdwroutine; struct FdwRoutine *fdwroutine;
void *fdw_private; void *fdw_private;
...@@ -653,7 +652,7 @@ typedef struct ForeignKeyOptInfo ...@@ -653,7 +652,7 @@ typedef struct ForeignKeyOptInfo
struct EquivalenceClass *eclass[INDEX_MAX_KEYS]; struct EquivalenceClass *eclass[INDEX_MAX_KEYS];
/* List of non-EC RestrictInfos matching each column's condition */ /* List of non-EC RestrictInfos matching each column's condition */
List *rinfos[INDEX_MAX_KEYS]; List *rinfos[INDEX_MAX_KEYS];
} ForeignKeyOptInfo; } ForeignKeyOptInfo;
/* /*
......
...@@ -93,8 +93,10 @@ typedef struct CachedPlanSource ...@@ -93,8 +93,10 @@ typedef struct CachedPlanSource
List *invalItems; /* other dependencies, as PlanInvalItems */ List *invalItems; /* other dependencies, as PlanInvalItems */
struct OverrideSearchPath *search_path; /* search_path used for struct OverrideSearchPath *search_path; /* search_path used for
* parsing and planning */ * parsing and planning */
Oid planUserId; /* User-id that the plan depends on */
MemoryContext query_context; /* context holding the above, or NULL */ MemoryContext query_context; /* context holding the above, or NULL */
Oid rewriteRoleId; /* Role ID we did rewriting for */
bool rewriteRowSecurity; /* row_security used during rewrite */
bool dependsOnRLS; /* is rewritten query specific to the above? */
/* If we have a generic plan, this is a reference-counted link to it: */ /* If we have a generic plan, this is a reference-counted link to it: */
struct CachedPlan *gplan; /* generic plan, or NULL if not valid */ struct CachedPlan *gplan; /* generic plan, or NULL if not valid */
/* Some state flags: */ /* Some state flags: */
...@@ -109,8 +111,6 @@ typedef struct CachedPlanSource ...@@ -109,8 +111,6 @@ typedef struct CachedPlanSource
double generic_cost; /* cost of generic plan, or -1 if not known */ double generic_cost; /* cost of generic plan, or -1 if not known */
double total_custom_cost; /* total cost of custom plans so far */ double total_custom_cost; /* total cost of custom plans so far */
int num_custom_plans; /* number of plans included in total */ int num_custom_plans; /* number of plans included in total */
bool hasRowSecurity; /* planned with row security? */
bool row_security_env; /* row security setting when planned */
} CachedPlanSource; } CachedPlanSource;
/* /*
...@@ -131,11 +131,12 @@ typedef struct CachedPlan ...@@ -131,11 +131,12 @@ typedef struct CachedPlan
bool is_oneshot; /* is it a "oneshot" plan? */ bool is_oneshot; /* is it a "oneshot" plan? */
bool is_saved; /* is CachedPlan in a long-lived context? */ bool is_saved; /* is CachedPlan in a long-lived context? */
bool is_valid; /* is the stmt_list currently valid? */ bool is_valid; /* is the stmt_list currently valid? */
Oid planRoleId; /* Role ID the plan was created for */
bool dependsOnRole; /* is plan specific to that role? */
TransactionId saved_xmin; /* if valid, replan when TransactionXmin TransactionId saved_xmin; /* if valid, replan when TransactionXmin
* changes from this value */ * changes from this value */
int generation; /* parent's generation number for this plan */ int generation; /* parent's generation number for this plan */
int refcount; /* count of live references to this struct */ int refcount; /* count of live references to this struct */
bool has_foreign_join; /* plan has pushed down a foreign join */
MemoryContext context; /* context containing this CachedPlan */ MemoryContext context; /* context containing this CachedPlan */
} CachedPlan; } CachedPlan;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment