pramsey / pgsql-ogr-fdw

PostgreSQL foreign data wrapper for OGR
MIT License
237 stars 34 forks source link

Spatial push-down fails for prepared statements #186

Closed pramsey closed 3 years ago

pramsey commented 4 years ago

Really for all cases where plan caching happens, but is most visible with a test using prepared statements.

Set up an FDW.

create extension postgis;
create extension ogr_fdw;

SET client_min_messages = debug2;

drop server IF EXISTS myserver cascade;

CREATE SERVER myserver
  FOREIGN DATA WRAPPER ogr_fdw
  OPTIONS (
    datasource 'WFS:http://services.ga.gov.au/gis/services/Waste_Management_Facilities/MapServer/WFSServer',
    format 'WFS',
    config_options 'CPL_DEBUG=ON' );

CREATE FOREIGN TABLE waste_management_facilities_waste_management_facilities (
  shape Geometry(Point,4283),
  name varchar(150)
) SERVER "myserver"
OPTIONS (layer 'Waste_Management_Facilities:Waste_Management_Facilities');

Then set up a prepared statement and run it 6 times.

prepare fdw(text) as SELECT STRING_AGG(name, ', ') FROM waste_management_facilities_waste_management_facilities where shape && $1;
explain (analyze, buffers) execute fdw(ST_GeomFromText('POLYGON(( -35.2 149,-35.2 148,-34.1 148,-34.1 149,-35.2 149))',4283));

On the 6th run the plan will change and the geometry will not be materialized as a Const anymore, so the FDW will not attempt to push it down. This might be because the generic plan wraps the prepared parameter in a CAST, hiding the fact that it's a constant.

Here's the 6th plan.

 Aggregate  (cost=1027.50..1027.51 rows=1 width=32) (actual time=14287.227..14287.227 rows=1 loops=1)
   ->  Foreign Scan on waste_management_facilities_waste_management_facilities  (cost=25.00..1025.00 rows=1000 width=318) (actual time=536.630..14287.168 rows=19 loops=1)
         Filter: (shape && ($1)::geometry)
         Rows Removed by Filter: 2266
 Planning Time: 1198.881 ms
 Execution Time: 15445.089 ms

If there's some way to figure out if the right hand side of the operator is constant, other than just looking for Const nodes, that would get us out of this bind.

pramsey commented 3 years ago

This is looking pretty intractable. The argument coming in on the right side of && is

(FuncExpr) $3 = {
  xpr = (type = T_FuncExpr)
  funcid = 639820
  funcresulttype = 639459
  funcretset = false
  funcvariadic = false
  funcformat = COERCE_IMPLICIT_CAST
  funccollid = 0
  inputcollid = 100
  args = 0x00007fdd468134b8
  location = -1
}

And the function being called is

oid             | 639820
proname         | geometry
pronamespace    | 2200
...
prorettype      | 639459
proargtypes     | 25
prosrc          | parse_WKT_lwgeom
probin          | $libdir/postgis-3.1

Basically, geometry(text). So something about this query is falling into our implicit PostGIS geometry(text) cast (what?) which means we don't have access to the constant anymore.