1 -- ===================================================================
3 -- ===================================================================
5 CREATE EXTENSION postgres_fdw;
7 CREATE SERVER testserver1 FOREIGN DATA WRAPPER postgres_fdw;
10 EXECUTE $$CREATE SERVER loopback FOREIGN DATA WRAPPER postgres_fdw
11 OPTIONS (dbname '$$||current_database()||$$',
12 port '$$||current_setting('port')||$$'
14 EXECUTE $$CREATE SERVER loopback2 FOREIGN DATA WRAPPER postgres_fdw
15 OPTIONS (dbname '$$||current_database()||$$',
16 port '$$||current_setting('port')||$$'
21 CREATE USER MAPPING FOR public SERVER testserver1
22 OPTIONS (user 'value', password 'value');
23 CREATE USER MAPPING FOR CURRENT_USER SERVER loopback;
24 CREATE USER MAPPING FOR CURRENT_USER SERVER loopback2;
26 -- ===================================================================
27 -- create objects used through FDW loopback server
28 -- ===================================================================
29 CREATE TYPE user_enum AS ENUM ('foo', 'bar', 'buz');
31 CREATE TABLE "S 1"."T 1" (
40 CONSTRAINT t1_pkey PRIMARY KEY ("C 1")
42 CREATE TABLE "S 1"."T 2" (
45 CONSTRAINT t2_pkey PRIMARY KEY (c1)
47 CREATE TABLE "S 1"."T 3" (
51 CONSTRAINT t3_pkey PRIMARY KEY (c1)
53 CREATE TABLE "S 1"."T 4" (
57 CONSTRAINT t4_pkey PRIMARY KEY (c1)
60 INSERT INTO "S 1"."T 1"
63 to_char(id, 'FM00000'),
64 '1970-01-01'::timestamptz + ((id % 100) || ' days')::interval,
65 '1970-01-01'::timestamp + ((id % 100) || ' days')::interval,
69 FROM generate_series(1, 1000) id;
70 INSERT INTO "S 1"."T 2"
72 'AAA' || to_char(id, 'FM000')
73 FROM generate_series(1, 100) id;
74 INSERT INTO "S 1"."T 3"
77 'AAA' || to_char(id, 'FM000')
78 FROM generate_series(1, 100) id;
79 DELETE FROM "S 1"."T 3" WHERE c1 % 2 != 0; -- delete for outer join tests
80 INSERT INTO "S 1"."T 4"
83 'AAA' || to_char(id, 'FM000')
84 FROM generate_series(1, 100) id;
85 DELETE FROM "S 1"."T 4" WHERE c1 % 3 != 0; -- delete for outer join tests
92 -- ===================================================================
93 -- create foreign tables
94 -- ===================================================================
95 CREATE FOREIGN TABLE ft1 (
103 c7 char(10) default 'ft1',
106 ALTER FOREIGN TABLE ft1 DROP COLUMN c0;
108 CREATE FOREIGN TABLE ft2 (
116 c7 char(10) default 'ft2',
119 ALTER FOREIGN TABLE ft2 DROP COLUMN cx;
121 CREATE FOREIGN TABLE ft4 (
125 ) SERVER loopback OPTIONS (schema_name 'S 1', table_name 'T 3');
127 CREATE FOREIGN TABLE ft5 (
131 ) SERVER loopback OPTIONS (schema_name 'S 1', table_name 'T 4');
133 CREATE FOREIGN TABLE ft6 (
137 ) SERVER loopback2 OPTIONS (schema_name 'S 1', table_name 'T 4');
139 -- A table with oids. CREATE FOREIGN TABLE doesn't support the
140 -- WITH OIDS option, but ALTER does.
141 CREATE FOREIGN TABLE ft_pg_type (
144 ) SERVER loopback OPTIONS (schema_name 'pg_catalog', table_name 'pg_type');
145 ALTER TABLE ft_pg_type SET WITH OIDS;
147 -- ===================================================================
148 -- tests for validator
149 -- ===================================================================
150 -- requiressl, krbsrvname and gsslib are omitted because they depend on
152 ALTER SERVER testserver1 OPTIONS (
153 use_remote_estimate 'false',
155 fdw_startup_cost '123.456',
156 fdw_tuple_cost '0.123',
158 connect_timeout 'value',
163 --client_encoding 'value',
164 application_name 'value',
165 --fallback_application_name 'value',
167 keepalives_idle 'value',
168 keepalives_interval 'value',
169 -- requiressl 'value',
170 sslcompression 'value',
176 --requirepeer 'value',
177 -- krbsrvname 'value',
179 --replication 'value'
182 -- Error, invalid list syntax
183 ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo; bar');
185 -- OK but gets a warning
186 ALTER SERVER testserver1 OPTIONS (ADD extensions 'foo, bar');
187 ALTER SERVER testserver1 OPTIONS (DROP extensions);
189 ALTER USER MAPPING FOR public SERVER testserver1
190 OPTIONS (DROP user, DROP password);
192 ALTER FOREIGN TABLE ft1 OPTIONS (schema_name 'S 1', table_name 'T 1');
193 ALTER FOREIGN TABLE ft2 OPTIONS (schema_name 'S 1', table_name 'T 1');
194 ALTER FOREIGN TABLE ft1 ALTER COLUMN c1 OPTIONS (column_name 'C 1');
195 ALTER FOREIGN TABLE ft2 ALTER COLUMN c1 OPTIONS (column_name 'C 1');
198 -- Test that alteration of server options causes reconnection
199 -- Remote's errors might be non-English, so hide them to ensure stable results
201 SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should work
202 ALTER SERVER loopback OPTIONS (SET dbname 'no such database');
203 SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should fail
206 EXECUTE $$ALTER SERVER loopback
207 OPTIONS (SET dbname '$$||current_database()||$$')$$;
210 SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should work again
212 -- Test that alteration of user mapping options causes reconnection
213 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback
214 OPTIONS (ADD user 'no such user');
215 SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should fail
216 ALTER USER MAPPING FOR CURRENT_USER SERVER loopback
218 SELECT c3, c4 FROM ft1 ORDER BY c3, c1 LIMIT 1; -- should work again
219 \set VERBOSITY default
221 -- Now we should be able to run ANALYZE.
222 -- To exercise multiple code paths, we use local stats on ft1
223 -- and remote-estimate mode on ft2.
225 ALTER FOREIGN TABLE ft2 OPTIONS (use_remote_estimate 'true');
227 -- ===================================================================
229 -- ===================================================================
230 -- single table without alias
231 EXPLAIN (COSTS OFF) SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10;
232 SELECT * FROM ft1 ORDER BY c3, c1 OFFSET 100 LIMIT 10;
233 -- single table with alias - also test that tableoid sort is not pushed to remote side
234 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
235 SELECT * FROM ft1 t1 ORDER BY t1.c3, t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
236 -- whole-row reference
237 EXPLAIN (VERBOSE, COSTS OFF) SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
238 SELECT t1 FROM ft1 t1 ORDER BY t1.c3, t1.c1 OFFSET 100 LIMIT 10;
240 SELECT * FROM ft1 WHERE false;
242 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
243 SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
244 -- with FOR UPDATE/SHARE
245 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE;
246 SELECT * FROM ft1 t1 WHERE c1 = 101 FOR UPDATE;
247 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE;
248 SELECT * FROM ft1 t1 WHERE c1 = 102 FOR SHARE;
250 SELECT COUNT(*) FROM ft1 t1;
252 SELECT * FROM ft1 t1 WHERE t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 <= 10) ORDER BY c1;
254 SELECT * FROM ft1 t1 WHERE t1.c3 = (SELECT MAX(c3) FROM ft2 t2) ORDER BY c1;
256 WITH t1 AS (SELECT * FROM ft1 WHERE c1 <= 10) SELECT t2.c1, t2.c2, t2.c3, t2.c4 FROM t1, ft2 t2 WHERE t1.c1 = t2.c1 ORDER BY t1.c1;
258 SELECT 'fixed', NULL FROM ft1 t1 WHERE c1 = 1;
259 -- Test forcing the remote server to produce sorted data for a merge join.
260 SET enable_hashjoin TO false;
261 SET enable_nestloop TO false;
262 -- inner join; expressions in the clauses appear in the equivalence class list
263 EXPLAIN (VERBOSE, COSTS OFF)
264 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;
265 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;
266 -- outer join; expressions in the clauses do not appear in equivalence class
267 -- list but no output change as compared to the previous query
268 EXPLAIN (VERBOSE, COSTS OFF)
269 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;
270 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;
271 -- A join between local table and foreign join. ORDER BY clause is added to the
272 -- foreign join so that the local table can be joined using merge join strategy.
273 EXPLAIN (VERBOSE, COSTS OFF)
274 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;
275 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;
276 -- Test similar to above, except that the full join prevents any equivalence
277 -- classes from being merged. This produces single relation equivalence classes
278 -- included in join restrictions.
279 EXPLAIN (VERBOSE, COSTS OFF)
280 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;
281 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;
282 -- Test similar to above with all full outer joins
283 EXPLAIN (VERBOSE, COSTS OFF)
284 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;
285 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;
286 RESET enable_hashjoin;
287 RESET enable_nestloop;
289 -- ===================================================================
290 -- WHERE with remotely-executable conditions
291 -- ===================================================================
292 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- Var, OpExpr(b), Const
293 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 = 0; -- BoolExpr
294 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- NullTest
295 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest
296 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr
297 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l)
298 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = c1!; -- OpExpr(r)
299 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr
300 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr
301 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef
302 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars
303 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote
304 -- parameterized remote path for foreign table
305 EXPLAIN (VERBOSE, COSTS OFF)
306 SELECT * FROM "S 1"."T 1" a, ft2 b WHERE a."C 1" = 47 AND b.c1 = a.c2;
307 SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
309 -- check both safe and unsafe join conditions
310 EXPLAIN (VERBOSE, COSTS OFF)
311 SELECT * FROM ft2 a, ft2 b
312 WHERE a.c2 = 6 AND b.c1 = a.c1 AND a.c8 = 'foo' AND b.c7 = upper(a.c7);
313 SELECT * FROM ft2 a, ft2 b
314 WHERE a.c2 = 6 AND b.c1 = a.c1 AND a.c8 = 'foo' AND b.c7 = upper(a.c7);
315 -- bug before 9.3.5 due to sloppy handling of remote-estimate parameters
316 SELECT * FROM ft1 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft2 WHERE c1 < 5));
317 SELECT * FROM ft2 WHERE c1 = ANY (ARRAY(SELECT c1 FROM ft1 WHERE c1 < 5));
318 -- we should not push order by clause with volatile expressions or unsafe
320 EXPLAIN (VERBOSE, COSTS OFF)
321 SELECT * FROM ft2 ORDER BY ft2.c1, random();
322 EXPLAIN (VERBOSE, COSTS OFF)
323 SELECT * FROM ft2 ORDER BY ft2.c1, ft2.c3 collate "C";
325 -- user-defined operator/function
326 CREATE FUNCTION postgres_fdw_abs(int) RETURNS int AS $$
330 $$ LANGUAGE plpgsql IMMUTABLE;
331 CREATE OPERATOR === (
338 -- built-in operators and functions can be shipped for remote execution
339 EXPLAIN (VERBOSE, COSTS OFF)
340 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2);
341 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2);
342 EXPLAIN (VERBOSE, COSTS OFF)
343 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2;
344 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2;
346 -- by default, user-defined ones cannot
347 EXPLAIN (VERBOSE, COSTS OFF)
348 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
349 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
350 EXPLAIN (VERBOSE, COSTS OFF)
351 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
352 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
354 -- but let's put them in an extension ...
355 ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_abs(int);
356 ALTER EXTENSION postgres_fdw ADD OPERATOR === (int, int);
357 ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw');
359 -- ... now they can be shipped
360 EXPLAIN (VERBOSE, COSTS OFF)
361 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
362 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2);
363 EXPLAIN (VERBOSE, COSTS OFF)
364 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
365 SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
367 -- ===================================================================
369 -- ===================================================================
370 -- Analyze ft4 and ft5 so that we have better statistics. These tables do not
371 -- have use_remote_estimate set.
376 EXPLAIN (VERBOSE, COSTS OFF)
377 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;
378 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;
380 EXPLAIN (VERBOSE, COSTS OFF)
381 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;
382 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;
384 EXPLAIN (VERBOSE, COSTS OFF)
385 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;
386 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;
387 -- left outer join three tables
388 EXPLAIN (VERBOSE, COSTS OFF)
389 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;
390 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;
391 -- left outer join + placement of clauses.
392 -- clauses within the nullable side are not pulled up, but top level clause on
393 -- non-nullable side is pushed into non-nullable side
394 EXPLAIN (VERBOSE, COSTS OFF)
395 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;
396 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;
397 -- clauses within the nullable side are not pulled up, but the top level clause
398 -- on nullable side is not pushed down into nullable side
399 EXPLAIN (VERBOSE, COSTS OFF)
400 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)
401 WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10;
402 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)
403 WHERE (t2.c1 < 10 OR t2.c1 IS NULL) AND t1.c1 < 10;
405 EXPLAIN (VERBOSE, COSTS OFF)
406 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;
407 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;
408 -- right outer join three tables
409 EXPLAIN (VERBOSE, COSTS OFF)
410 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;
411 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;
413 EXPLAIN (VERBOSE, COSTS OFF)
414 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;
415 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;
416 -- full outer join with restrictions on the joining relations
417 -- a. the joining relations are both base relations
418 EXPLAIN (VERBOSE, COSTS OFF)
419 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;
420 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;
421 EXPLAIN (VERBOSE, COSTS OFF)
422 SELECT 1 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 (TRUE) OFFSET 10 LIMIT 10;
423 SELECT 1 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 (TRUE) OFFSET 10 LIMIT 10;
424 -- b. one of the joining relations is a base relation and the other is a join
426 EXPLAIN (VERBOSE, COSTS OFF)
427 SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM ft4 t2 LEFT JOIN ft5 t3 ON (t2.c1 = t3.c1) WHERE (t2.c1 between 50 and 60)) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b;
428 SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM ft4 t2 LEFT JOIN ft5 t3 ON (t2.c1 = t3.c1) WHERE (t2.c1 between 50 and 60)) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b;
429 -- c. test deparsing the remote query as nested subqueries
430 EXPLAIN (VERBOSE, COSTS OFF)
431 SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b;
432 SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b;
433 -- d. test deparsing rowmarked relations as subqueries
434 EXPLAIN (VERBOSE, COSTS OFF)
435 SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM "S 1"."T 3" WHERE c1 = 50) t1 INNER JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (TRUE) ORDER BY t1.c1, ss.a, ss.b FOR UPDATE OF t1;
436 SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM "S 1"."T 3" WHERE c1 = 50) t1 INNER JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (TRUE) ORDER BY t1.c1, ss.a, ss.b FOR UPDATE OF t1;
437 -- full outer join + inner join
438 EXPLAIN (VERBOSE, COSTS OFF)
439 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;
440 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;
441 -- full outer join three tables
442 EXPLAIN (VERBOSE, COSTS OFF)
443 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;
444 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;
445 -- full outer join + right outer join
446 EXPLAIN (VERBOSE, COSTS OFF)
447 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;
448 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;
449 -- right outer join + full outer join
450 EXPLAIN (VERBOSE, COSTS OFF)
451 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;
452 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;
453 -- full outer join + left outer join
454 EXPLAIN (VERBOSE, COSTS OFF)
455 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;
456 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;
457 -- left outer join + full outer join
458 EXPLAIN (VERBOSE, COSTS OFF)
459 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;
460 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;
461 -- right outer join + left outer join
462 EXPLAIN (VERBOSE, COSTS OFF)
463 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;
464 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;
465 -- left outer join + right outer join
466 EXPLAIN (VERBOSE, COSTS OFF)
467 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;
468 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;
469 -- full outer join + WHERE clause, only matched rows
470 EXPLAIN (VERBOSE, COSTS OFF)
471 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;
472 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;
473 -- full outer join + WHERE clause with shippable extensions set
474 EXPLAIN (VERBOSE, COSTS OFF)
475 SELECT t1.c1, t2.c2, t1.c3 FROM ft1 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE postgres_fdw_abs(t1.c1) > 0 OFFSET 10 LIMIT 10;
476 ALTER SERVER loopback OPTIONS (DROP extensions);
477 -- full outer join + WHERE clause with shippable extensions not set
478 EXPLAIN (VERBOSE, COSTS OFF)
479 SELECT t1.c1, t2.c2, t1.c3 FROM ft1 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE postgres_fdw_abs(t1.c1) > 0 OFFSET 10 LIMIT 10;
480 ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw');
481 -- join two tables with FOR UPDATE clause
482 -- tests whole-row reference for row marks
483 EXPLAIN (VERBOSE, COSTS OFF)
484 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;
485 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;
486 EXPLAIN (VERBOSE, COSTS OFF)
487 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;
488 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;
489 -- join two tables with FOR SHARE clause
490 EXPLAIN (VERBOSE, COSTS OFF)
491 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;
492 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;
493 EXPLAIN (VERBOSE, COSTS OFF)
494 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;
495 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;
497 EXPLAIN (VERBOSE, COSTS OFF)
498 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;
499 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;
500 -- ctid with whole-row reference
501 EXPLAIN (VERBOSE, COSTS OFF)
502 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;
503 -- SEMI JOIN, not pushed down
504 EXPLAIN (VERBOSE, COSTS OFF)
505 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;
506 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;
507 -- ANTI JOIN, not pushed down
508 EXPLAIN (VERBOSE, COSTS OFF)
509 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;
510 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;
511 -- CROSS JOIN, not pushed down
512 EXPLAIN (VERBOSE, COSTS OFF)
513 SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
514 SELECT t1.c1, t2.c1 FROM ft1 t1 CROSS JOIN ft2 t2 ORDER BY t1.c1, t2.c1 OFFSET 100 LIMIT 10;
515 -- different server, not pushed down. No result expected.
516 EXPLAIN (VERBOSE, COSTS OFF)
517 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;
518 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;
519 -- unsafe join conditions (c8 has a UDT), not pushed down. Practically a CROSS
520 -- JOIN since c8 in both tables has same value.
521 EXPLAIN (VERBOSE, COSTS OFF)
522 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;
523 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;
524 -- unsafe conditions on one side (c8 has a UDT), not pushed down.
525 EXPLAIN (VERBOSE, COSTS OFF)
526 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;
527 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;
528 -- join where unsafe to pushdown condition in WHERE clause has a column not
529 -- in the SELECT clause. In this test unsafe clause needs to have column
530 -- references from both joining sides so that the clause is not pushed down
531 -- into one of the joining sides.
532 EXPLAIN (VERBOSE, COSTS OFF)
533 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;
534 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;
535 -- Aggregate after UNION, for testing setrefs
536 EXPLAIN (VERBOSE, COSTS OFF)
537 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;
538 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;
539 -- join with lateral reference
540 EXPLAIN (VERBOSE, COSTS OFF)
541 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;
542 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;
544 -- non-Var items in targetlist of the nullable rel of a join preventing
545 -- push-down in some cases
546 -- unable to push {ft1, ft2}
547 EXPLAIN (VERBOSE, COSTS OFF)
548 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;
549 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;
551 -- ok to push {ft1, ft2} but not {ft1, ft2, ft4}
552 EXPLAIN (VERBOSE, COSTS OFF)
553 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;
554 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;
556 -- join with nullable side with some columns with null values
557 UPDATE ft5 SET c3 = null where c1 % 9 = 0;
558 EXPLAIN (VERBOSE, COSTS OFF)
559 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;
560 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;
562 -- multi-way join involving multiple merge joins
563 -- (this case used to have EPQ-related planning problems)
564 SET enable_nestloop TO false;
565 SET enable_hashjoin TO false;
566 EXPLAIN (VERBOSE, COSTS OFF)
567 SELECT * FROM ft1, ft2, ft4, ft5 WHERE ft1.c1 = ft2.c1 AND ft1.c2 = ft4.c1
568 AND ft1.c2 = ft5.c1 AND ft1.c1 < 100 AND ft2.c1 < 100 FOR UPDATE;
569 SELECT * FROM ft1, ft2, ft4, ft5 WHERE ft1.c1 = ft2.c1 AND ft1.c2 = ft4.c1
570 AND ft1.c2 = ft5.c1 AND ft1.c1 < 100 AND ft2.c1 < 100 FOR UPDATE;
571 RESET enable_nestloop;
572 RESET enable_hashjoin;
574 -- check join pushdown in situations where multiple userids are involved
575 CREATE ROLE regress_view_owner SUPERUSER;
576 CREATE USER MAPPING FOR regress_view_owner SERVER loopback;
577 GRANT SELECT ON ft4 TO regress_view_owner;
578 GRANT SELECT ON ft5 TO regress_view_owner;
580 CREATE VIEW v4 AS SELECT * FROM ft4;
581 CREATE VIEW v5 AS SELECT * FROM ft5;
582 ALTER VIEW v5 OWNER TO regress_view_owner;
583 EXPLAIN (VERBOSE, COSTS OFF)
584 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
585 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;
586 ALTER VIEW v4 OWNER TO regress_view_owner;
587 EXPLAIN (VERBOSE, COSTS OFF)
588 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
589 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;
591 EXPLAIN (VERBOSE, COSTS OFF)
592 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
593 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;
594 ALTER VIEW v4 OWNER TO CURRENT_USER;
595 EXPLAIN (VERBOSE, COSTS OFF)
596 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
597 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;
598 ALTER VIEW v4 OWNER TO regress_view_owner;
601 DROP OWNED BY regress_view_owner;
602 DROP ROLE regress_view_owner;
605 -- ===================================================================
606 -- Aggregate and grouping queries
607 -- ===================================================================
610 explain (verbose, costs off)
611 select count(c6), sum(c1), avg(c1), min(c2), max(c1), stddev(c2), sum(c1) * (random() <= 1)::int as sum2 from ft1 where c2 < 5 group by c2 order by 1, 2;
612 select count(c6), sum(c1), avg(c1), min(c2), max(c1), stddev(c2), sum(c1) * (random() <= 1)::int as sum2 from ft1 where c2 < 5 group by c2 order by 1, 2;
614 -- Aggregate is not pushed down as aggregation contains random()
615 explain (verbose, costs off)
616 select sum(c1 * (random() <= 1)::int) as sum, avg(c1) from ft1;
618 -- Aggregate over join query
619 explain (verbose, costs off)
620 select count(*), sum(t1.c1), avg(t2.c1) from ft1 t1 inner join ft1 t2 on (t1.c2 = t2.c2) where t1.c2 = 6;
621 select count(*), sum(t1.c1), avg(t2.c1) from ft1 t1 inner join ft1 t2 on (t1.c2 = t2.c2) where t1.c2 = 6;
623 -- Not pushed down due to local conditions present in underneath input rel
624 explain (verbose, costs off)
625 select sum(t1.c1), count(t2.c1) from ft1 t1 inner join ft2 t2 on (t1.c1 = t2.c1) where ((t1.c1 * t2.c1)/(t1.c1 * t2.c1)) * random() <= 1;
627 -- GROUP BY clause having expressions
628 explain (verbose, costs off)
629 select c2/2, sum(c2) * (c2/2) from ft1 group by c2/2 order by c2/2;
630 select c2/2, sum(c2) * (c2/2) from ft1 group by c2/2 order by c2/2;
632 -- Aggregates in subquery are pushed down.
633 explain (verbose, costs off)
634 select count(x.a), sum(x.a) from (select c2 a, sum(c1) b from ft1 group by c2, sqrt(c1) order by 1, 2) x;
635 select count(x.a), sum(x.a) from (select c2 a, sum(c1) b from ft1 group by c2, sqrt(c1) order by 1, 2) x;
637 -- Aggregate is still pushed down by taking unshippable expression out
638 explain (verbose, costs off)
639 select c2 * (random() <= 1)::int as sum1, sum(c1) * c2 as sum2 from ft1 group by c2 order by 1, 2;
640 select c2 * (random() <= 1)::int as sum1, sum(c1) * c2 as sum2 from ft1 group by c2 order by 1, 2;
642 -- Aggregate with unshippable GROUP BY clause are not pushed
643 explain (verbose, costs off)
644 select c2 * (random() <= 1)::int as c2 from ft2 group by c2 * (random() <= 1)::int order by 1;
646 -- GROUP BY clause in various forms, cardinal, alias and constant expression
647 explain (verbose, costs off)
648 select count(c2) w, c2 x, 5 y, 7.0 z from ft1 group by 2, y, 9.0::int order by 2;
649 select count(c2) w, c2 x, 5 y, 7.0 z from ft1 group by 2, y, 9.0::int order by 2;
651 -- GROUP BY clause referring to same column multiple times
652 -- Also, ORDER BY contains an aggregate function
653 explain (verbose, costs off)
654 select c2, c2 from ft1 where c2 > 6 group by 1, 2 order by sum(c1);
655 select c2, c2 from ft1 where c2 > 6 group by 1, 2 order by sum(c1);
657 -- Testing HAVING clause shippability
658 explain (verbose, costs off)
659 select c2, sum(c1) from ft2 group by c2 having avg(c1) < 500 and sum(c1) < 49800 order by c2;
660 select c2, sum(c1) from ft2 group by c2 having avg(c1) < 500 and sum(c1) < 49800 order by c2;
662 -- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down
663 explain (verbose, costs off)
664 select count(*) from (select c5, count(c1) from ft1 group by c5, sqrt(c2) having (avg(c1) / avg(c1)) * random() <= 1 and avg(c1) < 500) x;
665 select count(*) from (select c5, count(c1) from ft1 group by c5, sqrt(c2) having (avg(c1) / avg(c1)) * random() <= 1 and avg(c1) < 500) x;
667 -- Aggregate in HAVING clause is not pushable, and thus aggregation is not pushed down
668 explain (verbose, costs off)
669 select sum(c1) from ft1 group by c2 having avg(c1 * (random() <= 1)::int) > 100 order by 1;
672 -- Testing ORDER BY, DISTINCT, FILTER, Ordered-sets and VARIADIC within aggregates
674 -- ORDER BY within aggregate, same column used to order
675 explain (verbose, costs off)
676 select array_agg(c1 order by c1) from ft1 where c1 < 100 group by c2 order by 1;
677 select array_agg(c1 order by c1) from ft1 where c1 < 100 group by c2 order by 1;
679 -- ORDER BY within aggregate, different column used to order also using DESC
680 explain (verbose, costs off)
681 select array_agg(c5 order by c1 desc) from ft2 where c2 = 6 and c1 < 50;
682 select array_agg(c5 order by c1 desc) from ft2 where c2 = 6 and c1 < 50;
684 -- DISTINCT within aggregate
685 explain (verbose, costs off)
686 select array_agg(distinct (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1;
687 select array_agg(distinct (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1;
689 -- DISTINCT combined with ORDER BY within aggregate
690 explain (verbose, costs off)
691 select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1;
692 select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1;
694 explain (verbose, costs off)
695 select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5 desc nulls last) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1;
696 select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5 desc nulls last) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1;
698 -- FILTER within aggregate
699 explain (verbose, costs off)
700 select sum(c1) filter (where c1 < 100 and c2 > 5) from ft1 group by c2 order by 1 nulls last;
701 select sum(c1) filter (where c1 < 100 and c2 > 5) from ft1 group by c2 order by 1 nulls last;
703 -- DISTINCT, ORDER BY and FILTER within aggregate
704 explain (verbose, costs off)
705 select sum(c1%3), sum(distinct c1%3 order by c1%3) filter (where c1%3 < 2), c2 from ft1 where c2 = 6 group by c2;
706 select sum(c1%3), sum(distinct c1%3 order by c1%3) filter (where c1%3 < 2), c2 from ft1 where c2 = 6 group by c2;
708 -- Outer query is aggregation query
709 explain (verbose, costs off)
710 select distinct (select count(*) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1;
711 select distinct (select count(*) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1;
712 -- Inner query is aggregation query
713 explain (verbose, costs off)
714 select distinct (select count(t1.c1) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1;
715 select distinct (select count(t1.c1) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1;
717 -- Aggregate not pushed down as FILTER condition is not pushable
718 explain (verbose, costs off)
719 select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 order by 1;
720 explain (verbose, costs off)
721 select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1;
723 -- Ordered-sets within aggregate
724 explain (verbose, costs off)
725 select c2, rank('10'::varchar) within group (order by c6), percentile_cont(c2/10::numeric) within group (order by c1) from ft1 where c2 < 10 group by c2 having percentile_cont(c2/10::numeric) within group (order by c1) < 500 order by c2;
726 select c2, rank('10'::varchar) within group (order by c6), percentile_cont(c2/10::numeric) within group (order by c1) from ft1 where c2 < 10 group by c2 having percentile_cont(c2/10::numeric) within group (order by c1) < 500 order by c2;
728 -- Using multiple arguments within aggregates
729 explain (verbose, costs off)
730 select c1, rank(c1, c2) within group (order by c1, c2) from ft1 group by c1, c2 having c1 = 6 order by 1;
731 select c1, rank(c1, c2) within group (order by c1, c2) from ft1 group by c1, c2 having c1 = 6 order by 1;
733 -- User defined function for user defined aggregate, VARIADIC
734 create function least_accum(anyelement, variadic anyarray)
735 returns anyelement language sql as
736 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)';
737 create aggregate least_agg(variadic items anyarray) (
738 stype = anyelement, sfunc = least_accum
741 -- Disable hash aggregation for plan stability.
742 set enable_hashagg to false;
744 -- Not pushed down due to user defined aggregate
745 explain (verbose, costs off)
746 select c2, least_agg(c1) from ft1 group by c2 order by c2;
748 -- Add function and aggregate into extension
749 alter extension postgres_fdw add function least_accum(anyelement, variadic anyarray);
750 alter extension postgres_fdw add aggregate least_agg(variadic items anyarray);
751 alter server loopback options (set extensions 'postgres_fdw');
753 -- Now aggregate will be pushed. Aggregate will display VARIADIC argument.
754 explain (verbose, costs off)
755 select c2, least_agg(c1) from ft1 where c2 < 100 group by c2 order by c2;
756 select c2, least_agg(c1) from ft1 where c2 < 100 group by c2 order by c2;
758 -- Remove function and aggregate from extension
759 alter extension postgres_fdw drop function least_accum(anyelement, variadic anyarray);
760 alter extension postgres_fdw drop aggregate least_agg(variadic items anyarray);
761 alter server loopback options (set extensions 'postgres_fdw');
763 -- Not pushed down as we have dropped objects from extension.
764 explain (verbose, costs off)
765 select c2, least_agg(c1) from ft1 group by c2 order by c2;
768 reset enable_hashagg;
769 drop aggregate least_agg(variadic items anyarray);
770 drop function least_accum(anyelement, variadic anyarray);
773 -- Testing USING OPERATOR() in ORDER BY within aggregate.
774 -- For this, we need user defined operators along with operator family and
775 -- operator class. Create those and then add them in extension. Note that
776 -- user defined objects are considered unshippable unless they are part of
778 create operator public.<^ (
784 create operator public.=^ (
790 create operator public.>^ (
796 create operator family my_op_family using btree;
798 create function my_op_cmp(a int, b int) returns int as
799 $$begin return btint4cmp(a, b); end $$ language plpgsql;
801 create operator class my_op_class for type int using btree family my_op_family as
802 operator 1 public.<^,
803 operator 3 public.=^,
804 operator 5 public.>^,
805 function 1 my_op_cmp(int, int);
807 -- This will not be pushed as user defined sort operator is not part of the
809 explain (verbose, costs off)
810 select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2;
812 -- Add into extension
813 alter extension postgres_fdw add operator class my_op_class using btree;
814 alter extension postgres_fdw add function my_op_cmp(a int, b int);
815 alter extension postgres_fdw add operator family my_op_family using btree;
816 alter extension postgres_fdw add operator public.<^(int, int);
817 alter extension postgres_fdw add operator public.=^(int, int);
818 alter extension postgres_fdw add operator public.>^(int, int);
819 alter server loopback options (set extensions 'postgres_fdw');
821 -- Now this will be pushed as sort operator is part of the extension.
822 explain (verbose, costs off)
823 select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2;
824 select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2;
826 -- Remove from extension
827 alter extension postgres_fdw drop operator class my_op_class using btree;
828 alter extension postgres_fdw drop function my_op_cmp(a int, b int);
829 alter extension postgres_fdw drop operator family my_op_family using btree;
830 alter extension postgres_fdw drop operator public.<^(int, int);
831 alter extension postgres_fdw drop operator public.=^(int, int);
832 alter extension postgres_fdw drop operator public.>^(int, int);
833 alter server loopback options (set extensions 'postgres_fdw');
835 -- This will not be pushed as sort operator is now removed from the extension.
836 explain (verbose, costs off)
837 select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2;
840 drop operator class my_op_class using btree;
841 drop function my_op_cmp(a int, b int);
842 drop operator family my_op_family using btree;
843 drop operator public.>^(int, int);
844 drop operator public.=^(int, int);
845 drop operator public.<^(int, int);
847 -- Input relation to aggregate push down hook is not safe to pushdown and thus
848 -- the aggregate cannot be pushed down to foreign server.
849 explain (verbose, costs off)
850 select count(t1.c3) from ft2 t1 left join ft2 t2 on (t1.c1 = random() * t2.c2);
852 -- Subquery in FROM clause having aggregate
853 explain (verbose, costs off)
854 select count(*), x.b from ft1, (select c2 a, sum(c1) b from ft1 group by c2) x where ft1.c2 = x.a group by x.b order by 1, 2;
855 select count(*), x.b from ft1, (select c2 a, sum(c1) b from ft1 group by c2) x where ft1.c2 = x.a group by x.b order by 1, 2;
857 -- FULL join with IS NULL check in HAVING
858 explain (verbose, costs off)
859 select avg(t1.c1), sum(t2.c1) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) group by t2.c1 having (avg(t1.c1) is null and sum(t2.c1) < 10) or sum(t2.c1) is null order by 1 nulls last, 2;
860 select avg(t1.c1), sum(t2.c1) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) group by t2.c1 having (avg(t1.c1) is null and sum(t2.c1) < 10) or sum(t2.c1) is null order by 1 nulls last, 2;
862 -- Aggregate over FULL join needing to deparse the joining relations as
864 explain (verbose, costs off)
865 select count(*), sum(t1.c1), avg(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);
866 select count(*), sum(t1.c1), avg(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);
868 -- ORDER BY expression is part of the target list but not pushed down to
870 explain (verbose, costs off)
871 select sum(c2) * (random() <= 1)::int as sum from ft1 order by 1;
872 select sum(c2) * (random() <= 1)::int as sum from ft1 order by 1;
874 -- LATERAL join, with parameterization
875 set enable_hashagg to false;
876 explain (verbose, costs off)
877 select c2, sum from "S 1"."T 1" t1, lateral (select sum(t2.c1 + t1."C 1") sum from ft2 t2 group by t2.c1) qry where t1.c2 * 2 = qry.sum and t1.c2 < 3 and t1."C 1" < 100 order by 1;
878 select c2, sum from "S 1"."T 1" t1, lateral (select sum(t2.c1 + t1."C 1") sum from ft2 t2 group by t2.c1) qry where t1.c2 * 2 = qry.sum and t1.c2 < 3 and t1."C 1" < 100 order by 1;
879 reset enable_hashagg;
881 -- Check with placeHolderVars
882 explain (verbose, costs off)
883 select sum(q.a), count(q.b) from ft4 left join (select 13, avg(ft1.c1), sum(ft2.c1) from ft1 right join ft2 on (ft1.c1 = ft2.c1)) q(a, b, c) on (ft4.c1 <= q.b);
884 select sum(q.a), count(q.b) from ft4 left join (select 13, avg(ft1.c1), sum(ft2.c1) from ft1 right join ft2 on (ft1.c1 = ft2.c1)) q(a, b, c) on (ft4.c1 <= q.b);
887 -- Not supported cases
889 explain (verbose, costs off)
890 select c2, sum(c1) from ft1 where c2 < 3 group by rollup(c2) order by 1 nulls last;
891 select c2, sum(c1) from ft1 where c2 < 3 group by rollup(c2) order by 1 nulls last;
892 explain (verbose, costs off)
893 select c2, sum(c1) from ft1 where c2 < 3 group by cube(c2) order by 1 nulls last;
894 select c2, sum(c1) from ft1 where c2 < 3 group by cube(c2) order by 1 nulls last;
895 explain (verbose, costs off)
896 select c2, c6, sum(c1) from ft1 where c2 < 3 group by grouping sets(c2, c6) order by 1 nulls last, 2 nulls last;
897 select c2, c6, sum(c1) from ft1 where c2 < 3 group by grouping sets(c2, c6) order by 1 nulls last, 2 nulls last;
898 explain (verbose, costs off)
899 select c2, sum(c1), grouping(c2) from ft1 where c2 < 3 group by c2 order by 1 nulls last;
900 select c2, sum(c1), grouping(c2) from ft1 where c2 < 3 group by c2 order by 1 nulls last;
902 -- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed
903 explain (verbose, costs off)
904 select distinct sum(c1)/1000 s from ft2 where c2 < 6 group by c2 order by 1;
905 select distinct sum(c1)/1000 s from ft2 where c2 < 6 group by c2 order by 1;
908 explain (verbose, costs off)
909 select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1;
910 select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1;
911 explain (verbose, costs off)
912 select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1;
913 select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1;
914 explain (verbose, costs off)
915 select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1;
916 select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1;
919 -- ===================================================================
920 -- parameterized queries
921 -- ===================================================================
923 PREPARE st1(int, int) AS SELECT t1.c3, t2.c3 FROM ft1 t1, ft2 t2 WHERE t1.c1 = $1 AND t2.c1 = $2;
924 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st1(1, 2);
926 EXECUTE st1(101, 101);
927 -- subquery using stable function (can't be sent to remote)
928 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;
929 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st2(10, 20);
931 EXECUTE st2(101, 121);
932 -- subquery using immutable function (can be sent to remote)
933 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;
934 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st3(10, 20);
937 -- custom plan should be chosen initially
938 PREPARE st4(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 = $1;
939 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
940 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
941 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
942 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
943 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
944 -- once we try it enough times, should switch to generic plan
945 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st4(1);
946 -- value of $1 should not be sent to remote
947 PREPARE st5(user_enum,int) AS SELECT * FROM ft1 t1 WHERE c8 = $1 and c1 = $2;
948 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
949 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
950 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
951 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
952 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
953 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
954 EXECUTE st5('foo', 1);
956 -- altering FDW options requires replanning
957 PREPARE st6 AS SELECT * FROM ft1 t1 WHERE t1.c1 = t1.c2;
958 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
959 PREPARE st7 AS INSERT INTO ft1 (c1,c2,c3) VALUES (1001,101,'foo');
960 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
961 ALTER TABLE "S 1"."T 1" RENAME TO "T 0";
962 ALTER FOREIGN TABLE ft1 OPTIONS (SET table_name 'T 0');
963 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
965 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
966 ALTER TABLE "S 1"."T 0" RENAME TO "T 1";
967 ALTER FOREIGN TABLE ft1 OPTIONS (SET table_name 'T 1');
969 PREPARE st8 AS SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2;
970 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
971 ALTER SERVER loopback OPTIONS (DROP extensions);
972 EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
974 ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw');
986 -- System columns, except ctid and oid, should not be sent to remote
987 EXPLAIN (VERBOSE, COSTS OFF)
988 SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1;
989 SELECT * FROM ft1 t1 WHERE t1.tableoid = 'ft1'::regclass LIMIT 1;
990 EXPLAIN (VERBOSE, COSTS OFF)
991 SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1;
992 SELECT tableoid::regclass, * FROM ft1 t1 LIMIT 1;
993 EXPLAIN (VERBOSE, COSTS OFF)
994 SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)';
995 SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)';
996 EXPLAIN (VERBOSE, COSTS OFF)
997 SELECT ctid, * FROM ft1 t1 LIMIT 1;
998 SELECT ctid, * FROM ft1 t1 LIMIT 1;
999 EXPLAIN (VERBOSE, COSTS OFF)
1000 SELECT oid, * FROM ft_pg_type WHERE typname = 'int4';
1001 SELECT oid, * FROM ft_pg_type WHERE typname = 'int4';
1003 -- ===================================================================
1004 -- used in PL/pgSQL function
1005 -- ===================================================================
1006 CREATE OR REPLACE FUNCTION f_test(p_c1 int) RETURNS int AS $$
1010 SELECT c1 INTO v_c1 FROM ft1 WHERE c1 = p_c1 LIMIT 1;
1011 PERFORM c1 FROM ft1 WHERE c1 = p_c1 AND p_c1 = v_c1 LIMIT 1;
1014 $$ LANGUAGE plpgsql;
1016 DROP FUNCTION f_test(int);
1018 -- ===================================================================
1020 -- ===================================================================
1021 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int;
1022 SELECT * FROM ft1 WHERE c1 = 1; -- ERROR
1023 SELECT ft1.c1, ft2.c2, ft1.c8 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR
1024 SELECT ft1.c1, ft2.c2, ft1 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR
1025 SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR
1026 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
1028 -- ===================================================================
1030 -- + local/remote error doesn't break cursor
1031 -- ===================================================================
1033 DECLARE c CURSOR FOR SELECT * FROM ft1 ORDER BY c1;
1040 SELECT * FROM ft1 WHERE 1 / (c1 - 1) > 0; -- ERROR
1043 SELECT * FROM ft1 ORDER BY c1 LIMIT 1;
1046 -- ===================================================================
1047 -- test handling of collations
1048 -- ===================================================================
1049 create table loct3 (f1 text collate "C" unique, f2 text, f3 varchar(10) unique);
1050 create foreign table ft3 (f1 text collate "C", f2 text, f3 varchar(10))
1051 server loopback options (table_name 'loct3', use_remote_estimate 'true');
1053 -- can be sent to remote
1054 explain (verbose, costs off) select * from ft3 where f1 = 'foo';
1055 explain (verbose, costs off) select * from ft3 where f1 COLLATE "C" = 'foo';
1056 explain (verbose, costs off) select * from ft3 where f2 = 'foo';
1057 explain (verbose, costs off) select * from ft3 where f3 = 'foo';
1058 explain (verbose, costs off) select * from ft3 f, loct3 l
1059 where f.f3 = l.f3 and l.f1 = 'foo';
1060 -- can't be sent to remote
1061 explain (verbose, costs off) select * from ft3 where f1 COLLATE "POSIX" = 'foo';
1062 explain (verbose, costs off) select * from ft3 where f1 = 'foo' COLLATE "C";
1063 explain (verbose, costs off) select * from ft3 where f2 COLLATE "C" = 'foo';
1064 explain (verbose, costs off) select * from ft3 where f2 = 'foo' COLLATE "C";
1065 explain (verbose, costs off) select * from ft3 f, loct3 l
1066 where f.f3 = l.f3 COLLATE "POSIX" and l.f1 = 'foo';
1068 -- ===================================================================
1069 -- test writable foreign table stuff
1070 -- ===================================================================
1071 EXPLAIN (verbose, costs off)
1072 INSERT INTO ft2 (c1,c2,c3) SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20;
1073 INSERT INTO ft2 (c1,c2,c3) SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20;
1074 INSERT INTO ft2 (c1,c2,c3)
1075 VALUES (1101,201,'aaa'), (1102,202,'bbb'), (1103,203,'ccc') RETURNING *;
1076 INSERT INTO ft2 (c1,c2,c3) VALUES (1104,204,'ddd'), (1105,205,'eee');
1077 EXPLAIN (verbose, costs off)
1078 UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3; -- can be pushed down
1079 UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3;
1080 EXPLAIN (verbose, costs off)
1081 UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *; -- can be pushed down
1082 UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *;
1083 EXPLAIN (verbose, costs off)
1084 UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
1085 FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9; -- can be pushed down
1086 UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
1087 FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
1088 EXPLAIN (verbose, costs off)
1089 DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4; -- can be pushed down
1090 DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
1091 EXPLAIN (verbose, costs off)
1092 DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2; -- can be pushed down
1093 DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
1094 SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1;
1095 EXPLAIN (verbose, costs off)
1096 INSERT INTO ft2 (c1,c2,c3) VALUES (9999,999,'foo') RETURNING tableoid::regclass;
1097 INSERT INTO ft2 (c1,c2,c3) VALUES (9999,999,'foo') RETURNING tableoid::regclass;
1098 EXPLAIN (verbose, costs off)
1099 UPDATE ft2 SET c3 = 'bar' WHERE c1 = 9999 RETURNING tableoid::regclass; -- can be pushed down
1100 UPDATE ft2 SET c3 = 'bar' WHERE c1 = 9999 RETURNING tableoid::regclass;
1101 EXPLAIN (verbose, costs off)
1102 DELETE FROM ft2 WHERE c1 = 9999 RETURNING tableoid::regclass; -- can be pushed down
1103 DELETE FROM ft2 WHERE c1 = 9999 RETURNING tableoid::regclass;
1105 -- Test UPDATE/DELETE with RETURNING on a three-table join
1106 INSERT INTO ft2 (c1,c2,c3)
1107 SELECT id, id - 1200, to_char(id, 'FM00000') FROM generate_series(1201, 1300) id;
1108 EXPLAIN (verbose, costs off)
1109 UPDATE ft2 SET c3 = 'foo'
1110 FROM ft4 INNER JOIN ft5 ON (ft4.c1 = ft5.c1)
1111 WHERE ft2.c1 > 1200 AND ft2.c2 = ft4.c1
1112 RETURNING ft2, ft2.*, ft4, ft4.*; -- can be pushed down
1113 UPDATE ft2 SET c3 = 'foo'
1114 FROM ft4 INNER JOIN ft5 ON (ft4.c1 = ft5.c1)
1115 WHERE ft2.c1 > 1200 AND ft2.c2 = ft4.c1
1116 RETURNING ft2, ft2.*, ft4, ft4.*;
1117 EXPLAIN (verbose, costs off)
1119 USING ft4 LEFT JOIN ft5 ON (ft4.c1 = ft5.c1)
1120 WHERE ft2.c1 > 1200 AND ft2.c1 % 10 = 0 AND ft2.c2 = ft4.c1
1121 RETURNING 100; -- can be pushed down
1123 USING ft4 LEFT JOIN ft5 ON (ft4.c1 = ft5.c1)
1124 WHERE ft2.c1 > 1200 AND ft2.c1 % 10 = 0 AND ft2.c2 = ft4.c1
1126 DELETE FROM ft2 WHERE ft2.c1 > 1200;
1128 -- Test UPDATE/DELETE with WHERE or JOIN/ON conditions containing
1129 -- user-defined operators/functions
1130 ALTER SERVER loopback OPTIONS (DROP extensions);
1131 INSERT INTO ft2 (c1,c2,c3)
1132 SELECT id, id % 10, to_char(id, 'FM00000') FROM generate_series(2001, 2010) id;
1133 EXPLAIN (verbose, costs off)
1134 UPDATE ft2 SET c3 = 'bar' WHERE postgres_fdw_abs(c1) > 2000 RETURNING *; -- can't be pushed down
1135 UPDATE ft2 SET c3 = 'bar' WHERE postgres_fdw_abs(c1) > 2000 RETURNING *;
1136 EXPLAIN (verbose, costs off)
1137 UPDATE ft2 SET c3 = 'baz'
1138 FROM ft4 INNER JOIN ft5 ON (ft4.c1 = ft5.c1)
1139 WHERE ft2.c1 > 2000 AND ft2.c2 === ft4.c1
1140 RETURNING ft2.*, ft4.*, ft5.*; -- can't be pushed down
1141 UPDATE ft2 SET c3 = 'baz'
1142 FROM ft4 INNER JOIN ft5 ON (ft4.c1 = ft5.c1)
1143 WHERE ft2.c1 > 2000 AND ft2.c2 === ft4.c1
1144 RETURNING ft2.*, ft4.*, ft5.*;
1145 EXPLAIN (verbose, costs off)
1147 USING ft4 INNER JOIN ft5 ON (ft4.c1 === ft5.c1)
1148 WHERE ft2.c1 > 2000 AND ft2.c2 = ft4.c1
1149 RETURNING ft2.c1, ft2.c2, ft2.c3; -- can't be pushed down
1151 USING ft4 INNER JOIN ft5 ON (ft4.c1 === ft5.c1)
1152 WHERE ft2.c1 > 2000 AND ft2.c2 = ft4.c1
1153 RETURNING ft2.c1, ft2.c2, ft2.c3;
1154 DELETE FROM ft2 WHERE ft2.c1 > 2000;
1155 ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw');
1157 -- Test that trigger on remote table works as expected
1158 CREATE OR REPLACE FUNCTION "S 1".F_BRTRIG() RETURNS trigger AS $$
1160 NEW.c3 = NEW.c3 || '_trig_update';
1163 $$ LANGUAGE plpgsql;
1164 CREATE TRIGGER t1_br_insert BEFORE INSERT OR UPDATE
1165 ON "S 1"."T 1" FOR EACH ROW EXECUTE PROCEDURE "S 1".F_BRTRIG();
1167 INSERT INTO ft2 (c1,c2,c3) VALUES (1208, 818, 'fff') RETURNING *;
1168 INSERT INTO ft2 (c1,c2,c3,c6) VALUES (1218, 818, 'ggg', '(--;') RETURNING *;
1169 UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *;
1171 -- Test errors thrown on remote side during update
1172 ALTER TABLE "S 1"."T 1" ADD CONSTRAINT c2positive CHECK (c2 >= 0);
1174 INSERT INTO ft1(c1, c2) VALUES(11, 12); -- duplicate key
1175 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT DO NOTHING; -- works
1176 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) DO NOTHING; -- unsupported
1177 INSERT INTO ft1(c1, c2) VALUES(11, 12) ON CONFLICT (c1, c2) DO UPDATE SET c3 = 'ffg'; -- unsupported
1178 INSERT INTO ft1(c1, c2) VALUES(1111, -2); -- c2positive
1179 UPDATE ft1 SET c2 = -c2 WHERE c1 = 1; -- c2positive
1181 -- Test savepoint/rollback behavior
1182 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1183 select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1;
1185 update ft2 set c2 = 42 where c2 = 0;
1186 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1188 update ft2 set c2 = 44 where c2 = 4;
1189 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1190 release savepoint s1;
1191 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1193 update ft2 set c2 = 46 where c2 = 6;
1194 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1195 rollback to savepoint s2;
1196 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1197 release savepoint s2;
1198 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1200 update ft2 set c2 = -2 where c2 = 42 and c1 = 10; -- fail on remote side
1201 rollback to savepoint s3;
1202 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1203 release savepoint s3;
1204 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1205 -- none of the above is committed yet remotely
1206 select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1;
1208 select c2, count(*) from ft2 where c2 < 500 group by 1 order by 1;
1209 select c2, count(*) from "S 1"."T 1" where c2 < 500 group by 1 order by 1;
1211 -- Above DMLs add data with c6 as NULL in ft1, so test ORDER BY NULLS LAST and NULLs
1212 -- FIRST behavior here.
1213 -- ORDER BY DESC NULLS LAST options
1214 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10;
1215 SELECT * FROM ft1 ORDER BY c6 DESC NULLS LAST, c1 OFFSET 795 LIMIT 10;
1216 -- ORDER BY DESC NULLS FIRST options
1217 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
1218 SELECT * FROM ft1 ORDER BY c6 DESC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
1219 -- ORDER BY ASC NULLS FIRST options
1220 EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
1221 SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10;
1223 -- ===================================================================
1224 -- test check constraints
1225 -- ===================================================================
1227 -- Consistent check constraints provide consistent results
1228 ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2positive CHECK (c2 >= 0);
1229 EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0;
1230 SELECT count(*) FROM ft1 WHERE c2 < 0;
1231 SET constraint_exclusion = 'on';
1232 EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0;
1233 SELECT count(*) FROM ft1 WHERE c2 < 0;
1234 RESET constraint_exclusion;
1235 -- check constraint is enforced on the remote side, not locally
1236 INSERT INTO ft1(c1, c2) VALUES(1111, -2); -- c2positive
1237 UPDATE ft1 SET c2 = -c2 WHERE c1 = 1; -- c2positive
1238 ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive;
1240 -- But inconsistent check constraints provide inconsistent results
1241 ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0);
1242 EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0;
1243 SELECT count(*) FROM ft1 WHERE c2 >= 0;
1244 SET constraint_exclusion = 'on';
1245 EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0;
1246 SELECT count(*) FROM ft1 WHERE c2 >= 0;
1247 RESET constraint_exclusion;
1248 -- local check constraint is not actually enforced
1249 INSERT INTO ft1(c1, c2) VALUES(1111, 2);
1250 UPDATE ft1 SET c2 = c2 + 1 WHERE c1 = 1;
1251 ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2negative;
1253 -- ===================================================================
1254 -- test WITH CHECK OPTION constraints
1255 -- ===================================================================
1257 CREATE TABLE base_tbl (a int, b int);
1258 CREATE FOREIGN TABLE foreign_tbl (a int, b int)
1259 SERVER loopback OPTIONS(table_name 'base_tbl');
1260 CREATE VIEW rw_view AS SELECT * FROM foreign_tbl
1261 WHERE a < b WITH CHECK OPTION;
1264 INSERT INTO rw_view VALUES (0, 10); -- ok
1265 INSERT INTO rw_view VALUES (10, 0); -- should fail
1266 EXPLAIN (VERBOSE, COSTS OFF)
1267 UPDATE rw_view SET b = 20 WHERE a = 0; -- not pushed down
1268 UPDATE rw_view SET b = 20 WHERE a = 0; -- ok
1269 EXPLAIN (VERBOSE, COSTS OFF)
1270 UPDATE rw_view SET b = -20 WHERE a = 0; -- not pushed down
1271 UPDATE rw_view SET b = -20 WHERE a = 0; -- should fail
1272 SELECT * FROM foreign_tbl;
1274 DROP FOREIGN TABLE foreign_tbl CASCADE;
1275 DROP TABLE base_tbl;
1277 -- ===================================================================
1278 -- test serial columns (ie, sequence-based defaults)
1279 -- ===================================================================
1280 create table loc1 (f1 serial, f2 text);
1281 create foreign table rem1 (f1 serial, f2 text)
1282 server loopback options(table_name 'loc1');
1283 select pg_catalog.setval('rem1_f1_seq', 10, false);
1284 insert into loc1(f2) values('hi');
1285 insert into rem1(f2) values('hi remote');
1286 insert into loc1(f2) values('bye');
1287 insert into rem1(f2) values('bye remote');
1291 -- ===================================================================
1292 -- test local triggers
1293 -- ===================================================================
1295 -- Trigger functions "borrowed" from triggers regress test.
1296 CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS $$
1298 RAISE NOTICE 'trigger_func(%) called: action = %, when = %, level = %',
1299 TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
1303 CREATE TRIGGER trig_stmt_before BEFORE DELETE OR INSERT OR UPDATE ON rem1
1304 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
1305 CREATE TRIGGER trig_stmt_after AFTER DELETE OR INSERT OR UPDATE ON rem1
1306 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
1308 CREATE OR REPLACE FUNCTION trigger_data() RETURNS trigger
1309 LANGUAGE plpgsql AS $$
1317 relid := TG_relid::regclass;
1319 for i in 0 .. TG_nargs - 1 loop
1321 argstr := argstr || ', ';
1323 argstr := argstr || TG_argv[i];
1326 RAISE NOTICE '%(%) % % % ON %',
1327 tg_name, argstr, TG_when, TG_level, TG_OP, relid;
1328 oldnew := '{}'::text[];
1329 if TG_OP != 'INSERT' then
1330 oldnew := array_append(oldnew, format('OLD: %s', OLD));
1333 if TG_OP != 'DELETE' then
1334 oldnew := array_append(oldnew, format('NEW: %s', NEW));
1337 RAISE NOTICE '%', array_to_string(oldnew, ',');
1339 if TG_OP = 'DELETE' then
1347 -- Test basic functionality
1348 CREATE TRIGGER trig_row_before
1349 BEFORE INSERT OR UPDATE OR DELETE ON rem1
1350 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1352 CREATE TRIGGER trig_row_after
1353 AFTER INSERT OR UPDATE OR DELETE ON rem1
1354 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1357 insert into rem1 values(1,'insert');
1358 update rem1 set f2 = 'update' where f1 = 1;
1359 update rem1 set f2 = f2 || f2;
1363 DROP TRIGGER trig_row_before ON rem1;
1364 DROP TRIGGER trig_row_after ON rem1;
1365 DROP TRIGGER trig_stmt_before ON rem1;
1366 DROP TRIGGER trig_stmt_after ON rem1;
1371 -- Test WHEN conditions
1373 CREATE TRIGGER trig_row_before_insupd
1374 BEFORE INSERT OR UPDATE ON rem1
1376 WHEN (NEW.f2 like '%update%')
1377 EXECUTE PROCEDURE trigger_data(23,'skidoo');
1379 CREATE TRIGGER trig_row_after_insupd
1380 AFTER INSERT OR UPDATE ON rem1
1382 WHEN (NEW.f2 like '%update%')
1383 EXECUTE PROCEDURE trigger_data(23,'skidoo');
1385 -- Insert or update not matching: nothing happens
1386 INSERT INTO rem1 values(1, 'insert');
1387 UPDATE rem1 set f2 = 'test';
1389 -- Insert or update matching: triggers are fired
1390 INSERT INTO rem1 values(2, 'update');
1391 UPDATE rem1 set f2 = 'update update' where f1 = '2';
1393 CREATE TRIGGER trig_row_before_delete
1394 BEFORE DELETE ON rem1
1396 WHEN (OLD.f2 like '%update%')
1397 EXECUTE PROCEDURE trigger_data(23,'skidoo');
1399 CREATE TRIGGER trig_row_after_delete
1400 AFTER DELETE ON rem1
1402 WHEN (OLD.f2 like '%update%')
1403 EXECUTE PROCEDURE trigger_data(23,'skidoo');
1405 -- Trigger is fired for f1=2, not for f1=1
1409 DROP TRIGGER trig_row_before_insupd ON rem1;
1410 DROP TRIGGER trig_row_after_insupd ON rem1;
1411 DROP TRIGGER trig_row_before_delete ON rem1;
1412 DROP TRIGGER trig_row_after_delete ON rem1;
1415 -- Test various RETURN statements in BEFORE triggers.
1417 CREATE FUNCTION trig_row_before_insupdate() RETURNS TRIGGER AS $$
1419 NEW.f2 := NEW.f2 || ' triggered !';
1422 $$ language plpgsql;
1424 CREATE TRIGGER trig_row_before_insupd
1425 BEFORE INSERT OR UPDATE ON rem1
1426 FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate();
1428 -- The new values should have 'triggered' appended
1429 INSERT INTO rem1 values(1, 'insert');
1431 INSERT INTO rem1 values(2, 'insert') RETURNING f2;
1433 UPDATE rem1 set f2 = '';
1435 UPDATE rem1 set f2 = 'skidoo' RETURNING f2;
1440 -- Add a second trigger, to check that the changes are propagated correctly
1441 -- from trigger to trigger
1442 CREATE TRIGGER trig_row_before_insupd2
1443 BEFORE INSERT OR UPDATE ON rem1
1444 FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate();
1446 INSERT INTO rem1 values(1, 'insert');
1448 INSERT INTO rem1 values(2, 'insert') RETURNING f2;
1450 UPDATE rem1 set f2 = '';
1452 UPDATE rem1 set f2 = 'skidoo' RETURNING f2;
1455 DROP TRIGGER trig_row_before_insupd ON rem1;
1456 DROP TRIGGER trig_row_before_insupd2 ON rem1;
1460 INSERT INTO rem1 VALUES (1, 'test');
1462 -- Test with a trigger returning NULL
1463 CREATE FUNCTION trig_null() RETURNS TRIGGER AS $$
1467 $$ language plpgsql;
1469 CREATE TRIGGER trig_null
1470 BEFORE INSERT OR UPDATE OR DELETE ON rem1
1471 FOR EACH ROW EXECUTE PROCEDURE trig_null();
1473 -- Nothing should have changed.
1474 INSERT INTO rem1 VALUES (2, 'test2');
1478 UPDATE rem1 SET f2 = 'test2';
1486 DROP TRIGGER trig_null ON rem1;
1489 -- Test a combination of local and remote triggers
1490 CREATE TRIGGER trig_row_before
1491 BEFORE INSERT OR UPDATE OR DELETE ON rem1
1492 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1494 CREATE TRIGGER trig_row_after
1495 AFTER INSERT OR UPDATE OR DELETE ON rem1
1496 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1498 CREATE TRIGGER trig_local_before BEFORE INSERT OR UPDATE ON loc1
1499 FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate();
1501 INSERT INTO rem1(f2) VALUES ('test');
1502 UPDATE rem1 SET f2 = 'testo';
1504 -- Test returning a system attribute
1505 INSERT INTO rem1(f2) VALUES ('test') RETURNING ctid;
1508 DROP TRIGGER trig_row_before ON rem1;
1509 DROP TRIGGER trig_row_after ON rem1;
1510 DROP TRIGGER trig_local_before ON loc1;
1513 -- Test direct foreign table modification functionality
1515 -- Test with statement-level triggers
1516 CREATE TRIGGER trig_stmt_before
1517 BEFORE DELETE OR INSERT OR UPDATE ON rem1
1518 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
1519 EXPLAIN (verbose, costs off)
1520 UPDATE rem1 set f2 = ''; -- can be pushed down
1521 EXPLAIN (verbose, costs off)
1522 DELETE FROM rem1; -- can be pushed down
1523 DROP TRIGGER trig_stmt_before ON rem1;
1525 CREATE TRIGGER trig_stmt_after
1526 AFTER DELETE OR INSERT OR UPDATE ON rem1
1527 FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
1528 EXPLAIN (verbose, costs off)
1529 UPDATE rem1 set f2 = ''; -- can be pushed down
1530 EXPLAIN (verbose, costs off)
1531 DELETE FROM rem1; -- can be pushed down
1532 DROP TRIGGER trig_stmt_after ON rem1;
1534 -- Test with row-level ON INSERT triggers
1535 CREATE TRIGGER trig_row_before_insert
1536 BEFORE INSERT ON rem1
1537 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1538 EXPLAIN (verbose, costs off)
1539 UPDATE rem1 set f2 = ''; -- can be pushed down
1540 EXPLAIN (verbose, costs off)
1541 DELETE FROM rem1; -- can be pushed down
1542 DROP TRIGGER trig_row_before_insert ON rem1;
1544 CREATE TRIGGER trig_row_after_insert
1545 AFTER INSERT ON rem1
1546 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1547 EXPLAIN (verbose, costs off)
1548 UPDATE rem1 set f2 = ''; -- can be pushed down
1549 EXPLAIN (verbose, costs off)
1550 DELETE FROM rem1; -- can be pushed down
1551 DROP TRIGGER trig_row_after_insert ON rem1;
1553 -- Test with row-level ON UPDATE triggers
1554 CREATE TRIGGER trig_row_before_update
1555 BEFORE UPDATE ON rem1
1556 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1557 EXPLAIN (verbose, costs off)
1558 UPDATE rem1 set f2 = ''; -- can't be pushed down
1559 EXPLAIN (verbose, costs off)
1560 DELETE FROM rem1; -- can be pushed down
1561 DROP TRIGGER trig_row_before_update ON rem1;
1563 CREATE TRIGGER trig_row_after_update
1564 AFTER UPDATE ON rem1
1565 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1566 EXPLAIN (verbose, costs off)
1567 UPDATE rem1 set f2 = ''; -- can't be pushed down
1568 EXPLAIN (verbose, costs off)
1569 DELETE FROM rem1; -- can be pushed down
1570 DROP TRIGGER trig_row_after_update ON rem1;
1572 -- Test with row-level ON DELETE triggers
1573 CREATE TRIGGER trig_row_before_delete
1574 BEFORE DELETE ON rem1
1575 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1576 EXPLAIN (verbose, costs off)
1577 UPDATE rem1 set f2 = ''; -- can be pushed down
1578 EXPLAIN (verbose, costs off)
1579 DELETE FROM rem1; -- can't be pushed down
1580 DROP TRIGGER trig_row_before_delete ON rem1;
1582 CREATE TRIGGER trig_row_after_delete
1583 AFTER DELETE ON rem1
1584 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1585 EXPLAIN (verbose, costs off)
1586 UPDATE rem1 set f2 = ''; -- can be pushed down
1587 EXPLAIN (verbose, costs off)
1588 DELETE FROM rem1; -- can't be pushed down
1589 DROP TRIGGER trig_row_after_delete ON rem1;
1591 -- ===================================================================
1592 -- test inheritance features
1593 -- ===================================================================
1595 CREATE TABLE a (aa TEXT);
1596 CREATE TABLE loct (aa TEXT, bb TEXT);
1597 CREATE FOREIGN TABLE b (bb TEXT) INHERITS (a)
1598 SERVER loopback OPTIONS (table_name 'loct');
1600 INSERT INTO a(aa) VALUES('aaa');
1601 INSERT INTO a(aa) VALUES('aaaa');
1602 INSERT INTO a(aa) VALUES('aaaaa');
1604 INSERT INTO b(aa) VALUES('bbb');
1605 INSERT INTO b(aa) VALUES('bbbb');
1606 INSERT INTO b(aa) VALUES('bbbbb');
1608 SELECT tableoid::regclass, * FROM a;
1609 SELECT tableoid::regclass, * FROM b;
1610 SELECT tableoid::regclass, * FROM ONLY a;
1612 UPDATE a SET aa = 'zzzzzz' WHERE aa LIKE 'aaaa%';
1614 SELECT tableoid::regclass, * FROM a;
1615 SELECT tableoid::regclass, * FROM b;
1616 SELECT tableoid::regclass, * FROM ONLY a;
1618 UPDATE b SET aa = 'new';
1620 SELECT tableoid::regclass, * FROM a;
1621 SELECT tableoid::regclass, * FROM b;
1622 SELECT tableoid::regclass, * FROM ONLY a;
1624 UPDATE a SET aa = 'newtoo';
1626 SELECT tableoid::regclass, * FROM a;
1627 SELECT tableoid::regclass, * FROM b;
1628 SELECT tableoid::regclass, * FROM ONLY a;
1632 SELECT tableoid::regclass, * FROM a;
1633 SELECT tableoid::regclass, * FROM b;
1634 SELECT tableoid::regclass, * FROM ONLY a;
1636 DROP TABLE a CASCADE;
1639 -- Check SELECT FOR UPDATE/SHARE with an inherited source table
1640 create table loct1 (f1 int, f2 int, f3 int);
1641 create table loct2 (f1 int, f2 int, f3 int);
1643 create table foo (f1 int, f2 int);
1644 create foreign table foo2 (f3 int) inherits (foo)
1645 server loopback options (table_name 'loct1');
1646 create table bar (f1 int, f2 int);
1647 create foreign table bar2 (f3 int) inherits (bar)
1648 server loopback options (table_name 'loct2');
1650 insert into foo values(1,1);
1651 insert into foo values(3,3);
1652 insert into foo2 values(2,2,2);
1653 insert into foo2 values(4,4,4);
1654 insert into bar values(1,11);
1655 insert into bar values(2,22);
1656 insert into bar values(6,66);
1657 insert into bar2 values(3,33,33);
1658 insert into bar2 values(4,44,44);
1659 insert into bar2 values(7,77,77);
1661 explain (verbose, costs off)
1662 select * from bar where f1 in (select f1 from foo) for update;
1663 select * from bar where f1 in (select f1 from foo) for update;
1665 explain (verbose, costs off)
1666 select * from bar where f1 in (select f1 from foo) for share;
1667 select * from bar where f1 in (select f1 from foo) for share;
1669 -- Check UPDATE with inherited target and an inherited source table
1670 explain (verbose, costs off)
1671 update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
1672 update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
1674 select tableoid::regclass, * from bar order by 1,2;
1676 -- Check UPDATE with inherited target and an appendrel subquery
1677 explain (verbose, costs off)
1678 update bar set f2 = f2 + 100
1680 ( select f1 from foo union all select f1+3 from foo ) ss
1681 where bar.f1 = ss.f1;
1682 update bar set f2 = f2 + 100
1684 ( select f1 from foo union all select f1+3 from foo ) ss
1685 where bar.f1 = ss.f1;
1687 select tableoid::regclass, * from bar order by 1,2;
1689 -- Test forcing the remote server to produce sorted data for a merge join,
1690 -- but the foreign table is an inheritance child.
1691 truncate table loct1;
1692 truncate table only foo;
1693 \set num_rows_foo 2000
1694 insert into loct1 select generate_series(0, :num_rows_foo, 2), generate_series(0, :num_rows_foo, 2), generate_series(0, :num_rows_foo, 2);
1695 insert into foo select generate_series(1, :num_rows_foo, 2), generate_series(1, :num_rows_foo, 2);
1696 SET enable_hashjoin to false;
1697 SET enable_nestloop to false;
1698 alter foreign table foo2 options (use_remote_estimate 'true');
1699 create index i_loct1_f1 on loct1(f1);
1700 create index i_foo_f1 on foo(f1);
1703 -- inner join; expressions in the clauses appear in the equivalence class list
1704 explain (verbose, costs off)
1705 select foo.f1, loct1.f1 from foo join loct1 on (foo.f1 = loct1.f1) order by foo.f2 offset 10 limit 10;
1706 select foo.f1, loct1.f1 from foo join loct1 on (foo.f1 = loct1.f1) order by foo.f2 offset 10 limit 10;
1707 -- outer join; expressions in the clauses do not appear in equivalence class
1708 -- list but no output change as compared to the previous query
1709 explain (verbose, costs off)
1710 select foo.f1, loct1.f1 from foo left join loct1 on (foo.f1 = loct1.f1) order by foo.f2 offset 10 limit 10;
1711 select foo.f1, loct1.f1 from foo left join loct1 on (foo.f1 = loct1.f1) order by foo.f2 offset 10 limit 10;
1712 RESET enable_hashjoin;
1713 RESET enable_nestloop;
1715 -- Test that WHERE CURRENT OF is not supported
1717 declare c cursor for select * from bar where f1 = 7;
1719 update bar set f2 = null where current of c;
1722 explain (verbose, costs off)
1723 delete from foo where f1 < 5 returning *;
1724 delete from foo where f1 < 5 returning *;
1725 explain (verbose, costs off)
1726 update bar set f2 = f2 + 100 returning *;
1727 update bar set f2 = f2 + 100 returning *;
1729 -- Test that UPDATE/DELETE with inherited target works with row-level triggers
1730 CREATE TRIGGER trig_row_before
1731 BEFORE UPDATE OR DELETE ON bar2
1732 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1734 CREATE TRIGGER trig_row_after
1735 AFTER UPDATE OR DELETE ON bar2
1736 FOR EACH ROW EXECUTE PROCEDURE trigger_data(23,'skidoo');
1738 explain (verbose, costs off)
1739 update bar set f2 = f2 + 100;
1740 update bar set f2 = f2 + 100;
1742 explain (verbose, costs off)
1743 delete from bar where f2 < 400;
1744 delete from bar where f2 < 400;
1747 drop table foo cascade;
1748 drop table bar cascade;
1752 -- ===================================================================
1753 -- test IMPORT FOREIGN SCHEMA
1754 -- ===================================================================
1756 CREATE SCHEMA import_source;
1757 CREATE TABLE import_source.t1 (c1 int, c2 varchar NOT NULL);
1758 CREATE TABLE import_source.t2 (c1 int default 42, c2 varchar NULL, c3 text collate "POSIX");
1759 CREATE TYPE typ1 AS (m1 int, m2 varchar);
1760 CREATE TABLE import_source.t3 (c1 timestamptz default now(), c2 typ1);
1761 CREATE TABLE import_source."x 4" (c1 float8, "C 2" text, c3 varchar(42));
1762 CREATE TABLE import_source."x 5" (c1 float8);
1763 ALTER TABLE import_source."x 5" DROP COLUMN c1;
1764 CREATE TABLE import_source.t4 (c1 int) PARTITION BY RANGE (c1);
1765 CREATE TABLE import_source.t4_part PARTITION OF import_source.t4
1766 FOR VALUES FROM (1) TO (100);
1768 CREATE SCHEMA import_dest1;
1769 IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest1;
1770 \det+ import_dest1.*
1774 CREATE SCHEMA import_dest2;
1775 IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest2
1776 OPTIONS (import_default 'true');
1777 \det+ import_dest2.*
1779 CREATE SCHEMA import_dest3;
1780 IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest3
1781 OPTIONS (import_collate 'false', import_not_null 'false');
1782 \det+ import_dest3.*
1785 -- Check LIMIT TO and EXCEPT
1786 CREATE SCHEMA import_dest4;
1787 IMPORT FOREIGN SCHEMA import_source LIMIT TO (t1, nonesuch)
1788 FROM SERVER loopback INTO import_dest4;
1789 \det+ import_dest4.*
1790 IMPORT FOREIGN SCHEMA import_source EXCEPT (t1, "x 4", nonesuch)
1791 FROM SERVER loopback INTO import_dest4;
1792 \det+ import_dest4.*
1794 -- Assorted error cases
1795 IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest4;
1796 IMPORT FOREIGN SCHEMA nonesuch FROM SERVER loopback INTO import_dest4;
1797 IMPORT FOREIGN SCHEMA nonesuch FROM SERVER loopback INTO notthere;
1798 IMPORT FOREIGN SCHEMA nonesuch FROM SERVER nowhere INTO notthere;
1800 -- Check case of a type present only on the remote server.
1801 -- We can fake this by dropping the type locally in our transaction.
1802 CREATE TYPE "Colors" AS ENUM ('red', 'green', 'blue');
1803 CREATE TABLE import_source.t5 (c1 int, c2 text collate "C", "Col" "Colors");
1805 CREATE SCHEMA import_dest5;
1807 DROP TYPE "Colors" CASCADE;
1808 IMPORT FOREIGN SCHEMA import_source LIMIT TO (t5)
1809 FROM SERVER loopback INTO import_dest5; -- ERROR
1816 CREATE SERVER fetch101 FOREIGN DATA WRAPPER postgres_fdw OPTIONS( fetch_size '101' );
1819 FROM pg_foreign_server
1820 WHERE srvname = 'fetch101'
1821 AND srvoptions @> array['fetch_size=101'];
1823 ALTER SERVER fetch101 OPTIONS( SET fetch_size '202' );
1826 FROM pg_foreign_server
1827 WHERE srvname = 'fetch101'
1828 AND srvoptions @> array['fetch_size=101'];
1831 FROM pg_foreign_server
1832 WHERE srvname = 'fetch101'
1833 AND srvoptions @> array['fetch_size=202'];
1835 CREATE FOREIGN TABLE table30000 ( x int ) SERVER fetch101 OPTIONS ( fetch_size '30000' );
1838 FROM pg_foreign_table
1839 WHERE ftrelid = 'table30000'::regclass
1840 AND ftoptions @> array['fetch_size=30000'];
1842 ALTER FOREIGN TABLE table30000 OPTIONS ( SET fetch_size '60000');
1845 FROM pg_foreign_table
1846 WHERE ftrelid = 'table30000'::regclass
1847 AND ftoptions @> array['fetch_size=30000'];
1850 FROM pg_foreign_table
1851 WHERE ftrelid = 'table30000'::regclass
1852 AND ftoptions @> array['fetch_size=60000'];
1856 -- ===================================================================
1857 -- test partition-wise-joins
1858 -- ===================================================================
1859 SET enable_partition_wise_join=on;
1861 CREATE TABLE fprt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
1862 CREATE TABLE fprt1_p1 (LIKE fprt1);
1863 CREATE TABLE fprt1_p2 (LIKE fprt1);
1864 INSERT INTO fprt1_p1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 249, 2) i;
1865 INSERT INTO fprt1_p2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(250, 499, 2) i;
1866 CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (0) TO (250)
1867 SERVER loopback OPTIONS (table_name 'fprt1_p1', use_remote_estimate 'true');
1868 CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (250) TO (500)
1869 SERVER loopback OPTIONS (TABLE_NAME 'fprt1_p2');
1874 CREATE TABLE fprt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
1875 CREATE TABLE fprt2_p1 (LIKE fprt2);
1876 CREATE TABLE fprt2_p2 (LIKE fprt2);
1877 INSERT INTO fprt2_p1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 249, 3) i;
1878 INSERT INTO fprt2_p2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(250, 499, 3) i;
1879 CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (0) TO (250)
1880 SERVER loopback OPTIONS (table_name 'fprt2_p1', use_remote_estimate 'true');
1881 CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (250) TO (500)
1882 SERVER loopback OPTIONS (table_name 'fprt2_p2', use_remote_estimate 'true');
1887 -- inner join three tables
1889 SELECT t1.a,t2.b,t3.c FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) INNER JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a % 25 =0 ORDER BY 1,2,3;
1890 SELECT t1.a,t2.b,t3.c FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.a = t2.b) INNER JOIN fprt1 t3 ON (t2.b = t3.a) WHERE t1.a % 25 =0 ORDER BY 1,2,3;
1892 -- left outer join + nullable clasue
1894 SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
1895 SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
1897 -- with whole-row reference
1899 SELECT t1,t2 FROM fprt1 t1 JOIN fprt2 t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a % 25 =0 ORDER BY 1,2;
1900 SELECT t1,t2 FROM fprt1 t1 JOIN fprt2 t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a % 25 =0 ORDER BY 1,2;
1902 -- join with lateral reference
1904 SELECT t1.a,t1.b FROM fprt1 t1, LATERAL (SELECT t2.a, t2.b FROM fprt2 t2 WHERE t1.a = t2.b AND t1.b = t2.a) q WHERE t1.a%25 = 0 ORDER BY 1,2;
1905 SELECT t1.a,t1.b FROM fprt1 t1, LATERAL (SELECT t2.a, t2.b FROM fprt2 t2 WHERE t1.a = t2.b AND t1.b = t2.a) q WHERE t1.a%25 = 0 ORDER BY 1,2;
1907 RESET enable_partition_wise_join;