fix: geojson invalid query on unavailable PostGIS
Closes https://github.com/PostgREST/postgrest/issues/4245. It adds a query on schema cache construction, but doesn't add a new attribute to the SchemaCache type. We only need the information to be passed to the `initialMediaHandlers` function to build the builtin media handlers.
This commit is contained in:
committed by
Steve Chavez
parent
7870add603
commit
0f1ca8faac
@@ -18,6 +18,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Fix OpenAPI broken docs link by @taimoorzaeem in #4080
|
||||
- Fix OpenAPI specification incorrectly exposing GET methods for volatile functions by @joelonsql in #4174
|
||||
- Fix empty spread embeddings return unexpected SQL error by @taimoorzaeem in #3887
|
||||
- Fix `Accept: application/geo+json` generating an invalid query when PostGIS is not available by @steve-chavez in #4245
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ querySchemaCache conf@AppConfig{..} = do
|
||||
reps <- SQL.statement conf $ dataRepresentations prepared
|
||||
mHdlers <- SQL.statement conf $ mediaHandlers prepared
|
||||
tzones <- SQL.statement mempty $ timezones prepared
|
||||
hasPgis <- SQL.statement conf $ postgisFunc prepared
|
||||
_ <-
|
||||
let sleepCall = SQL.Statement "select pg_sleep($1 / 1000.0)" (param HE.int4) HD.noResult prepared in
|
||||
whenJust configInternalSCSleep (`SQL.statement` sleepCall) -- only used for testing
|
||||
@@ -164,7 +165,7 @@ querySchemaCache conf@AppConfig{..} = do
|
||||
, dbRelationships = getOverrideRelationshipsMap rels cRels
|
||||
, dbRoutines = funcs
|
||||
, dbRepresentations = reps
|
||||
, dbMediaHandlers = HM.union mHdlers initialMediaHandlers -- the custom handlers will override the initial ones
|
||||
, dbMediaHandlers = HM.union mHdlers $ initialMediaHandlers hasPgis -- the custom handlers will override the initial ones
|
||||
, dbTimezones = tzones
|
||||
}
|
||||
where
|
||||
@@ -1048,12 +1049,14 @@ allViewsKeyDependencies =
|
||||
having ncol = array_length(array_agg(row(col.attname, view_columns) order by pks_fks.ord), 1)
|
||||
|]
|
||||
|
||||
initialMediaHandlers :: MediaHandlerMap
|
||||
initialMediaHandlers =
|
||||
initialMediaHandlers :: Bool -> MediaHandlerMap
|
||||
initialMediaHandlers hasPostgisFunc =
|
||||
HM.insert (RelAnyElement, MediaType.MTAny ) (BuiltinOvAggJson, MediaType.MTApplicationJSON) $
|
||||
HM.insert (RelAnyElement, MediaType.MTApplicationJSON) (BuiltinOvAggJson, MediaType.MTApplicationJSON) $
|
||||
HM.insert (RelAnyElement, MediaType.MTTextCSV ) (BuiltinOvAggCsv, MediaType.MTTextCSV) $
|
||||
HM.insert (RelAnyElement, MediaType.MTGeoJSON ) (BuiltinOvAggGeoJson, MediaType.MTGeoJSON)
|
||||
(if hasPostgisFunc
|
||||
then HM.insert (RelAnyElement, MediaType.MTGeoJSON ) (BuiltinOvAggGeoJson, MediaType.MTGeoJSON)
|
||||
else mempty)
|
||||
HM.empty
|
||||
|
||||
mediaHandlers :: Bool -> SQL.Statement AppConfig MediaHandlerMap
|
||||
@@ -1139,6 +1142,35 @@ timezones = SQL.Statement sql HE.noParams decodeTimezones
|
||||
decodeTimezones :: HD.Result TimezoneNames
|
||||
decodeTimezones = S.fromList <$> HD.rowList (column HD.text)
|
||||
|
||||
|
||||
-- Find the postgis function that has the signature:
|
||||
-- st_asgeojson(record,...) returns text
|
||||
postgisFunc :: Bool -> SQL.Statement AppConfig Bool
|
||||
postgisFunc = SQL.Statement sql params decoder
|
||||
where
|
||||
params =
|
||||
(map escapeIdent . toList . configDbSchemas >$< arrayParam HE.text) <>
|
||||
(map escapeIdent . toList . configDbExtraSearchPath >$< arrayParam HE.text)
|
||||
decoder = HD.singleRow (column HD.bool)
|
||||
sql = encodeUtf8 [trimming|
|
||||
SELECT
|
||||
exists(
|
||||
SELECT
|
||||
1
|
||||
FROM pg_catalog.pg_proc AS p
|
||||
JOIN pg_catalog.pg_depend AS d
|
||||
ON d.objid = p.oid
|
||||
AND d.deptype = 'e'
|
||||
JOIN pg_catalog.pg_extension AS e
|
||||
ON e.oid = d.refobjid
|
||||
WHERE p.pronamespace = ANY($$1::regnamespace[] || $$2::regnamespace[])
|
||||
AND p.proname = 'st_asgeojson'
|
||||
AND e.extname = 'postgis'
|
||||
AND p.proargtypes[0] = 'record'::regtype
|
||||
AND pg_get_function_result(p.oid) = 'text'
|
||||
);
|
||||
|]
|
||||
|
||||
param :: HE.Value a -> HE.Params a
|
||||
param = HE.param . HE.nonNullable
|
||||
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
- - tag: BuiltinOvAggCsv
|
||||
- tag: MTTextCSV
|
||||
|
||||
- - - tag: RelAnyElement
|
||||
- tag: MTGeoJSON
|
||||
- - tag: BuiltinOvAggGeoJson
|
||||
- tag: MTGeoJSON
|
||||
|
||||
- - - tag: RelAnyElement
|
||||
- tag: MTApplicationJSON
|
||||
- - tag: BuiltinOvAggJson
|
||||
|
||||
@@ -1137,6 +1137,18 @@ def test_no_pool_connection_required_on_bad_embedding(defaultenv):
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_no_pool_connection_required_on_unavailable_postgis(defaultenv):
|
||||
"no pool connection should be consumed when PostGIS is not available, the request should be quickly rejected at the plan level"
|
||||
|
||||
headers = {
|
||||
"Accept": "application/geo+json",
|
||||
}
|
||||
|
||||
with run(env=defaultenv, no_pool_connection_available=True) as postgrest:
|
||||
response = postgrest.session.get("/projects", headers=headers)
|
||||
assert response.status_code == 406
|
||||
|
||||
|
||||
# https://github.com/PostgREST/postgrest/issues/2620
|
||||
def test_notify_reloading_catalog_cache(defaultenv):
|
||||
"notify should reload the connection catalog cache"
|
||||
|
||||
Reference in New Issue
Block a user