postgres_fdw: Refuse to send FETCH FIRST WITH TIES to remote servers.
authorEtsuro Fujita <[email protected]>
Fri, 7 Jun 2024 08:45:06 +0000 (17:45 +0900)
committerEtsuro Fujita <[email protected]>
Fri, 7 Jun 2024 08:45:06 +0000 (17:45 +0900)
Previously, when considering LIMIT pushdown, postgres_fdw failed to
check whether the query has this clause, which led to pushing false
LIMIT clauses, causing incorrect results.

This clause has been supported since v13, so we need to do a
remote-version check before deciding that it will be safe to push such a
clause, but we do not currently have a way to do the check (without
accessing the remote server); disable pushing such a clause for now.

Oversight in commit 357889eb1.  Back-patch to v13, where that commit
added the support.

Per bug #18467 from Onder Kalaci.

Patch by Japin Li, per a suggestion from Tom Lane, with some changes to
the comments by me.  Review by Onder Kalaci, Alvaro Herrera, and me.

Discussion: https://postgr.es/m/18467-7bb89084ff03a08d%40postgresql.org

contrib/postgres_fdw/expected/postgres_fdw.out
contrib/postgres_fdw/postgres_fdw.c
contrib/postgres_fdw/sql/postgres_fdw.sql

index b29ebfe8d8c2e3379de283a4d8415c800f71fee3..73a75d09fface669886f5849d3898a7457864f49 100644 (file)
@@ -1041,6 +1041,27 @@ SELECT * FROM ft1 t1 WHERE t1.c1 === t1.c2 order by t1.c2 limit 1;
   1 |  1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1  | 1          | foo
 (1 row)
 
+-- Ensure we don't ship FETCH FIRST .. WITH TIES
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.c2 FROM ft1 t1 WHERE t1.c1 > 960 ORDER BY t1.c2 FETCH FIRST 2 ROWS WITH TIES;
+                                           QUERY PLAN                                            
+-------------------------------------------------------------------------------------------------
+ Limit
+   Output: c2
+   ->  Foreign Scan on public.ft1 t1
+         Output: c2
+         Remote SQL: SELECT c2 FROM "S 1"."T 1" WHERE (("C 1" > 960)) ORDER BY c2 ASC NULLS LAST
+(5 rows)
+
+SELECT t1.c2 FROM ft1 t1 WHERE t1.c1 > 960 ORDER BY t1.c2 FETCH FIRST 2 ROWS WITH TIES;
+ c2 
+----
+  0
+  0
+  0
+  0
+(4 rows)
+
 -- check schema-qualification of regconfig constant
 CREATE TEXT SEARCH CONFIGURATION public.custom_search
   (COPY = pg_catalog.english);
index e58f686e2c67a30a4cbce9d5f2c371dba7d922d8..f5a4c1d5835c39a1751a0c17f59b22e1a14631d0 100644 (file)
@@ -6843,6 +6843,20 @@ add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
    if (ifpinfo->local_conds)
        return;
 
+   /*
+    * If the query has FETCH FIRST .. WITH TIES, 1) it must have ORDER BY as
+    * well, which is used to determine which additional rows tie for the last
+    * place in the result set, and 2) ORDER BY must already have been
+    * determined to be safe to push down before we get here.  So in that case
+    * the FETCH clause is safe to push down with ORDER BY if the remote
+    * server is v13 or later, but if not, the remote query will fail entirely
+    * for lack of support for it.  Since we do not currently have a way to do
+    * a remote-version check (without accessing the remote server), disable
+    * pushing the FETCH clause for now.
+    */
+   if (parse->limitOption == LIMIT_OPTION_WITH_TIES)
+       return;
+
    /*
     * Also, the LIMIT/OFFSET cannot be pushed down, if their expressions are
     * not safe to remote.
index ee5edee79746b01ba1fd69094eabd26a4669197f..a62e1fbc33eea55d90ab843cc9cc5bb960393cd2 100644 (file)
@@ -402,6 +402,11 @@ EXPLAIN (VERBOSE, COSTS OFF)
   SELECT * FROM ft1 t1 WHERE t1.c1 === t1.c2 order by t1.c2 limit 1;
 SELECT * FROM ft1 t1 WHERE t1.c1 === t1.c2 order by t1.c2 limit 1;
 
+-- Ensure we don't ship FETCH FIRST .. WITH TIES
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT t1.c2 FROM ft1 t1 WHERE t1.c1 > 960 ORDER BY t1.c2 FETCH FIRST 2 ROWS WITH TIES;
+SELECT t1.c2 FROM ft1 t1 WHERE t1.c1 > 960 ORDER BY t1.c2 FETCH FIRST 2 ROWS WITH TIES;
+
 -- check schema-qualification of regconfig constant
 CREATE TEXT SEARCH CONFIGURATION public.custom_search
   (COPY = pg_catalog.english);