From 869818e9819b25fc0bcda6b917eda12f470c433a Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Thu, 13 Nov 2025 16:50:01 -0500 Subject: [PATCH] Revert "fix: handle queries on non-existing table gracefully " Temporarily revert commit 390ba199329c75f034e23e3c9029fdb22ec8ff2c to help with debugging https://github.com/PostgREST/postgrest/issues/4462 --- docs/references/errors.rst | 4 - src/PostgREST/Error.hs | 16 ---- src/PostgREST/Plan.hs | 57 ++++++-------- src/PostgREST/Response.hs | 4 +- src/PostgREST/SchemaCache/Identifiers.hs | 1 - test/io/test_big_schema.py | 4 +- test/io/test_io.py | 78 ++++++++++--------- test/spec/Feature/ConcurrentSpec.hs | 8 +- test/spec/Feature/Query/DeleteSpec.hs | 7 +- test/spec/Feature/Query/ErrorSpec.hs | 6 +- test/spec/Feature/Query/InsertSpec.hs | 2 +- test/spec/Feature/Query/MultipleSchemaSpec.hs | 8 +- test/spec/Feature/Query/QuerySpec.hs | 29 +++---- test/spec/Feature/Query/UpdateSpec.hs | 10 +-- 14 files changed, 99 insertions(+), 135 deletions(-) diff --git a/docs/references/errors.rst b/docs/references/errors.rst index 1802828579..4654ba331e 100644 --- a/docs/references/errors.rst +++ b/docs/references/errors.rst @@ -305,10 +305,6 @@ Related to a :ref:`schema_cache`. Most of the time, these errors are solved by : | | | in the ``columns`` query parameter is not found. | | PGRST204 | | | +---------------+-------------+-------------------------------------------------------------+ -| .. _pgrst205: | 404 | Caused when the :ref:`table specified ` in | -| | | the URI is not found. | -| PGRST205 | | | -+---------------+-------------+-------------------------------------------------------------+ .. _pgrst3**: diff --git a/src/PostgREST/Error.hs b/src/PostgREST/Error.hs index 39217610cf..53d01abc67 100644 --- a/src/PostgREST/Error.hs +++ b/src/PostgREST/Error.hs @@ -49,7 +49,6 @@ import PostgREST.SchemaCache.Relationship (Cardinality (..), RelationshipsMap) import PostgREST.SchemaCache.Routine (Routine (..), RoutineParam (..)) -import PostgREST.SchemaCache.Table (Table (..)) import Protolude @@ -250,7 +249,6 @@ data SchemaCacheError | NoRelBetween Text Text (Maybe Text) Text RelationshipsMap | NoRpc Text Text [Text] MediaType Bool [QualifiedIdentifier] [Routine] | ColumnNotFound Text Text - | TableNotFound Text Text [Table] deriving Show instance PgrstError SchemaCacheError where @@ -259,7 +257,6 @@ instance PgrstError SchemaCacheError where status NoRelBetween{} = HTTP.status400 status NoRpc{} = HTTP.status404 status ColumnNotFound{} = HTTP.status400 - status TableNotFound{} = HTTP.status404 headers _ = mempty @@ -269,7 +266,6 @@ instance ErrorBody SchemaCacheError where code NoRpc{} = "PGRST202" code AmbiguousRpc{} = "PGRST203" code ColumnNotFound{} = "PGRST204" - code TableNotFound{} = "PGRST205" message (NoRelBetween parent child _ _ _) = "Could not find a relationship between '" <> parent <> "' and '" <> child <> "' in the schema cache" message (AmbiguousRelBetween parent child _) = "Could not embed because more than one relationship was found for '" <> parent <> "' and '" <> child <> "'" @@ -282,7 +278,6 @@ instance ErrorBody SchemaCacheError where fmtPrms p = if null argumentKeys then " without parameters" else p message (AmbiguousRpc procs) = "Could not choose the best candidate function between: " <> T.intercalate ", " [pdSchema p <> "." <> pdName p <> "(" <> T.intercalate ", " [ppName a <> " => " <> ppType a | a <- pdParams p] <> ")" | p <- procs] message (ColumnNotFound rel col) = "Could not find the '" <> col <> "' column of '" <> rel <> "' in the schema cache" - message (TableNotFound schemaName relName _) = "Could not find the table '" <> schemaName <> "." <> relName <> "' in the schema cache" details (NoRelBetween parent child embedHint schema _) = Just $ JSON.String $ "Searched for a foreign key relationship between '" <> parent <> "' and '" <> child <> maybe mempty ("' using the hint '" <>) embedHint <> "' in the schema '" <> schema <> "', but no matches were found." details (AmbiguousRelBetween _ _ rels) = Just $ JSON.toJSONList (compressedRel <$> rels) @@ -313,7 +308,6 @@ instance ErrorBody SchemaCacheError where where onlySingleParams = isInvPost && contentType `elem` [MTTextPlain, MTTextXML, MTOctetStream] hint (AmbiguousRpc _) = Just "Try renaming the parameters or the function itself in the database so function overloading can be resolved" - hint (TableNotFound schemaName relName tbls) = JSON.String <$> tableNotFoundHint schemaName relName tbls hint _ = Nothing @@ -426,16 +420,6 @@ noRpcHint schema procName params allProcs overloadedProcs = | null overloadedProcs = Fuzzy.getOne fuzzySetOfProcs procName | otherwise = (procName <>) <$> Fuzzy.getOne fuzzySetOfParams (listToText params) --- | --- Do a fuzzy search in all tables in the same schema and return closest result -tableNotFoundHint :: Text -> Text -> [Table] -> Maybe Text -tableNotFoundHint schema tblName tblList - = fmap (\tbl -> "Perhaps you meant the table '" <> schema <> "." <> tbl <> "'") perhapsTable - where - perhapsTable = Fuzzy.getOne fuzzyTableSet tblName - fuzzyTableSet = Fuzzy.fromList [ tableName tbl | tbl <- tblList, tableSchema tbl == schema] - - compressedRel :: Relationship -> JSON.Value -- An ambiguousness error cannot happen for computed relationships TODO refactor so this mempty is not needed compressedRel ComputedRelationship{} = JSON.object mempty diff --git a/src/PostgREST/Plan.hs b/src/PostgREST/Plan.hs index df62291525..eb7f61ea6b 100644 --- a/src/PostgREST/Plan.hs +++ b/src/PostgREST/Plan.hs @@ -34,7 +34,6 @@ import qualified PostgREST.SchemaCache.Routine as Routine import Data.Either.Combinators (mapLeft, mapRight) import Data.List (delete, lookup) -import Data.Maybe (fromJust) import Data.Tree (Tree (..)) import PostgREST.ApiRequest (ApiRequest (..)) @@ -172,20 +171,18 @@ dbActionPlan dbAct conf apiReq sCache = case dbAct of wrappedReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> Bool -> Either Error CrudPlan wrappedReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{..},..} headersOnly = do - qi <- findTable identifier (dbTables sCache) - rPlan <- readPlan qi conf sCache apiRequest - (handler, mediaType) <- mapLeft ApiRequestError $ negotiateContent conf apiRequest qi iAcceptMediaType (dbMediaHandlers sCache) (hasDefaultSelect rPlan) + rPlan <- readPlan identifier conf sCache apiRequest + (handler, mediaType) <- mapLeft ApiRequestError $ negotiateContent conf apiRequest identifier iAcceptMediaType (dbMediaHandlers sCache) (hasDefaultSelect rPlan) if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right () - return $ WrappedReadPlan rPlan SQL.Read handler mediaType headersOnly qi + return $ WrappedReadPlan rPlan SQL.Read handler mediaType headersOnly identifier mutateReadPlan :: Mutation -> ApiRequest -> QualifiedIdentifier -> AppConfig -> SchemaCache -> Either Error CrudPlan mutateReadPlan mutation apiRequest@ApiRequest{iPreferences=Preferences{..},..} identifier conf sCache = do - qi <- findTable identifier (dbTables sCache) - rPlan <- readPlan qi conf sCache apiRequest - mPlan <- mutatePlan mutation qi apiRequest sCache rPlan + rPlan <- readPlan identifier conf sCache apiRequest + mPlan <- mutatePlan mutation identifier apiRequest sCache rPlan if not (null invalidPrefs) && preferHandling == Just Strict then Left $ ApiRequestError $ InvalidPreferences invalidPrefs else Right () - (handler, mediaType) <- mapLeft ApiRequestError $ negotiateContent conf apiRequest qi iAcceptMediaType (dbMediaHandlers sCache) (hasDefaultSelect rPlan) - return $ MutateReadPlan rPlan mPlan SQL.Write handler mediaType mutation qi + (handler, mediaType) <- mapLeft ApiRequestError $ negotiateContent conf apiRequest identifier iAcceptMediaType (dbMediaHandlers sCache) (hasDefaultSelect rPlan) + return $ MutateReadPlan rPlan mPlan SQL.Write handler mediaType mutation identifier callReadPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> InvokeMethod -> Either Error CrudPlan callReadPlan identifier conf sCache apiRequest@ApiRequest{iPreferences=Preferences{preferHandling, invalidPrefs, preferMaxAffected},..} invMethod = do @@ -809,13 +806,6 @@ validateAggFunctions aggFunctionsAllowed (Node rp@ReadPlan {select} forest) | not aggFunctionsAllowed && any (isJust . csAggFunction) select = Left $ ApiRequestError AggregatesNotAllowed | otherwise = Node rp <$> traverse (validateAggFunctions aggFunctionsAllowed) forest --- | Lookup table in the schema cache before creating read plan -findTable :: QualifiedIdentifier -> TablesMap -> Either Error QualifiedIdentifier -findTable qi@QualifiedIdentifier{..} tableMap = - case HM.lookup qi tableMap of - Nothing -> Left $ SchemaCacheErr $ TableNotFound qiSchema qiName (HM.elems tableMap) - Just _ -> Right qi - addFilters :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either Error ReadPlanTree addFilters ctx ApiRequest{..} rReq = foldr addFilterToNode (Right rReq) flts @@ -992,7 +982,21 @@ updateNode f (targetNodeName:remainingPath, a) (Right (Node rootNode forest)) = findNode = find (\(Node ReadPlan{relName, relAlias} _) -> relName == targetNodeName || relAlias == Just targetNodeName) forest mutatePlan :: Mutation -> QualifiedIdentifier -> ApiRequest -> SchemaCache -> ReadPlanTree -> Either Error MutatePlan -mutatePlan mutation qi ApiRequest{iPreferences=Preferences{..}, ..} SchemaCache{dbTables, dbRepresentations} readReq = +mutatePlan mutation qi ApiRequest{iPreferences=Preferences{..}, ..} SchemaCache{dbTables, dbRepresentations} readReq = do + tbl <- maybe (Left $ ApiRequestError InvalidResourcePath) Right $ HM.lookup qi dbTables + let ctx = ResolverContext dbTables dbRepresentations qi "json" + QueryParams.QueryParams{..} = iQueryParams + pkCols = tablePKCols tbl + confCols = fromMaybe pkCols qsOnConflict + returnings = + if preferRepresentation == Just None || isNothing preferRepresentation + then [] + else S.toList $ inferColsEmbedNeeds readReq pkCols + logic = map (resolveLogicTree ctx . snd) qsLogic + combinedLogic = foldr (addFilterToLogicForest . resolveFilter ctx) logic qsFiltersRoot + body = payRaw <$> iPayload -- the body is assumed to be json at this stage(ApiRequest validates) + applyDefaults = preferMissing == Just ApplyDefaults + typedColumnsOrError = resolveOrError ctx tbl `traverse` S.toList iColumns case mutation of MutationCreate -> mapRight (\typedColumns -> Insert qi typedColumns body ((,) <$> preferResolution <*> Just confCols) [] returnings pkCols applyDefaults) typedColumnsOrError @@ -1009,23 +1013,6 @@ mutatePlan mutation qi ApiRequest{iPreferences=Preferences{..}, ..} SchemaCache{ else Left $ ApiRequestError InvalidFilters MutationDelete -> Right $ Delete qi combinedLogic returnings - where - ctx = ResolverContext dbTables dbRepresentations qi "json" - confCols = fromMaybe pkCols qsOnConflict - QueryParams.QueryParams{..} = iQueryParams - returnings = - if preferRepresentation == Just None || isNothing preferRepresentation - then [] - else S.toList $ inferColsEmbedNeeds readReq pkCols - -- TODO: remove fromJust by refactoring later - -- we can use fromJust, we have already looked up the table before building mutatePlan - tbl = fromJust $ HM.lookup qi dbTables - pkCols = maybe mempty tablePKCols (Just tbl) - logic = map (resolveLogicTree ctx . snd) qsLogic - combinedLogic = foldr (addFilterToLogicForest . resolveFilter ctx) logic qsFiltersRoot - body = payRaw <$> iPayload -- the body is assumed to be json at this stage(ApiRequest validates) - applyDefaults = preferMissing == Just ApplyDefaults - typedColumnsOrError = resolveOrError ctx tbl `traverse` S.toList iColumns resolveOrError :: ResolverContext -> Table -> FieldName -> Either Error CoercibleField resolveOrError ctx table field = case resolveTableFieldName table field Nothing of diff --git a/src/PostgREST/Response.hs b/src/PostgREST/Response.hs index 065c722f71..9f1770870e 100644 --- a/src/PostgREST/Response.hs +++ b/src/PostgREST/Response.hs @@ -209,10 +209,10 @@ actionResponse (MaybeDbResult InspectPlan{ipHdrsOnly=headersOnly} body) _ versio in Right $ PgrstResponse HTTP.status200 (MediaType.toContentType MTOpenAPI : cLHeader ++ maybeToList (profileHeader schema negotiatedByProfile)) rsBody -actionResponse (NoDbResult (RelInfoPlan qi@QualifiedIdentifier{..})) _ _ _ SchemaCache{dbTables} _ _ = +actionResponse (NoDbResult (RelInfoPlan qi)) _ _ _ SchemaCache{dbTables} _ _ = case HM.lookup qi dbTables of Just tbl -> respondInfo $ allowH tbl - Nothing -> Left $ Error.SchemaCacheErr $ Error.TableNotFound qiSchema qiName (HM.elems dbTables) + Nothing -> Left $ Error.ApiRequestError Error.InvalidResourcePath where allowH table = let hasPK = not . null $ tablePKCols table in diff --git a/src/PostgREST/SchemaCache/Identifiers.hs b/src/PostgREST/SchemaCache/Identifiers.hs index e5b541eb6a..c09eb30d30 100644 --- a/src/PostgREST/SchemaCache/Identifiers.hs +++ b/src/PostgREST/SchemaCache/Identifiers.hs @@ -25,7 +25,6 @@ instance Hashable RelIdentifier -- | Represents a pg identifier with a prepended schema name "schema.table". -- When qiSchema is "", the schema is defined by the pg search_path. --- TODO: Refactor this, we also use QI for procedure names data QualifiedIdentifier = QualifiedIdentifier { qiSchema :: Schema , qiName :: TableName diff --git a/test/io/test_big_schema.py b/test/io/test_big_schema.py index ae8e8a9902..b355e3a08d 100644 --- a/test/io/test_big_schema.py +++ b/test/io/test_big_schema.py @@ -67,6 +67,6 @@ def test_should_not_fail_with_stack_overflow(defaultenv): with run(env=env, wait_max_seconds=30) as postgrest: response = postgrest.session.get("/unknown-table?select=unknown-rel(*)") - assert response.status_code == 404 + assert response.status_code == 400 data = response.json() - assert data["code"] == "PGRST205" + assert data["code"] == "PGRST200" diff --git a/test/io/test_io.py b/test/io/test_io.py index 21749767c6..75d72c9e63 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -937,6 +937,15 @@ def test_admin_works_with_host_special_values(specialhostvalue, defaultenv): def test_log_level(level, defaultenv): "log_level should filter request logging" + def drain_stdout(proc): + lines = [] + while True: + chunk = proc.read_stdout(nlines=20) + if not chunk: + break + lines.extend(chunk) + return lines + env = {**defaultenv, "PGRST_LOG_LEVEL": level} # any token to test 500 response for "Server lacks JWT secret" @@ -953,57 +962,56 @@ def test_log_level(level, defaultenv): response = postgrest.session.get("/") assert response.status_code == 200 - output = sorted(postgrest.read_stdout(nlines=7)) + output = drain_stdout(postgrest) + http_lines = [line for line in output if line.startswith("- - ")] + + def has_match(pattern): + return any(re.match(pattern, line) for line in http_lines) if level == "crit": assert len(output) == 0 elif level == "error": - assert re.match( - r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"', - output[0], + assert has_match( + r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+' ) - assert len(output) == 1 + assert len(http_lines) == 1 elif level == "warn": - assert re.match( - r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"', - output[0], + assert has_match( + r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+' ) - assert re.match( - r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"', - output[1], + assert has_match( + r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+' ) - assert len(output) == 2 + assert len(http_lines) == 2 elif level == "info": - assert re.match( - r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"', - output[0], + assert has_match( + r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+' ) - assert re.match( - r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"', - output[1], + assert has_match( + r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+' ) - assert re.match( - r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"', - output[2], + assert has_match( + r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+' ) - assert len(output) == 3 + assert len(http_lines) == 3 elif level == "debug": - assert re.match( - r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+"', - output[0], + assert has_match( + r'- - - \[.+\] "GET / HTTP/1.1" 500 \d+ "" "python-requests/.+' ) - assert re.match( - r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+"', - output[1], + assert has_match( + r'- - postgrest_test_anonymous \[.+\] "GET / HTTP/1.1" 200 \d+ "" "python-requests/.+' ) - assert re.match( - r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+"', - output[2], + assert has_match( + r'- - postgrest_test_anonymous \[.+\] "GET /unknown HTTP/1.1" 404 \d+ "" "python-requests/.+' ) - - assert len(output) == 7 - assert any("Connection" and "is available" in line for line in output) - assert any("Connection" and "is used" in line for line in output) + assert len(http_lines) == 3 + connection_lines = [line for line in output if "Connection" in line] + available_lines = [ + line for line in connection_lines if "is available" in line + ] + used_lines = [line for line in connection_lines if "is used" in line] + assert len(available_lines) == 2 + assert len(used_lines) == 2 @pytest.mark.parametrize("level", ["crit", "error", "warn", "info", "debug"]) diff --git a/test/spec/Feature/ConcurrentSpec.hs b/test/spec/Feature/ConcurrentSpec.hs index 0c77a060d1..d52d54666f 100644 --- a/test/spec/Feature/ConcurrentSpec.hs +++ b/test/spec/Feature/ConcurrentSpec.hs @@ -24,8 +24,12 @@ spec = it "should not raise 'transaction in progress' error" $ raceTest 10 $ get "/fakefake" - `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fakefake' in the schema cache"} |] + `shouldRespondWith` [json| + { "hint": null, + "details":null, + "code":"42P01", + "message":"relation \"test.fakefake\" does not exist" + } |] { matchStatus = 404 , matchHeaders = [] } diff --git a/test/spec/Feature/Query/DeleteSpec.hs b/test/spec/Feature/Query/DeleteSpec.hs index 5a3d18928a..1724c8dd5e 100644 --- a/test/spec/Feature/Query/DeleteSpec.hs +++ b/test/spec/Feature/Query/DeleteSpec.hs @@ -114,12 +114,7 @@ spec = context "totally unknown route" $ it "fails with 404" $ - request methodDelete "/foozle?id=eq.101" [] "" - `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.foo'","message":"Could not find the table 'test.foozle' in the schema cache"} |] - { matchStatus = 404 - , matchHeaders = [] - } + request methodDelete "/foozle?id=eq.101" [] "" `shouldRespondWith` 404 context "table with limited privileges" $ do it "fails deleting the row when return=representation and selecting all the columns" $ diff --git a/test/spec/Feature/Query/ErrorSpec.hs b/test/spec/Feature/Query/ErrorSpec.hs index da6f9cb824..185810f90c 100644 --- a/test/spec/Feature/Query/ErrorSpec.hs +++ b/test/spec/Feature/Query/ErrorSpec.hs @@ -42,10 +42,10 @@ pgErrorCodeMapping = do it "works with SchemaCache error" $ get "/non_existent_table" `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.collision_test_table'","message":"Could not find the table 'test.non_existent_table' in the schema cache"} |] + [json| {"code":"42P01","details":null,"hint":null,"message":"relation \"test.non_existent_table\" does not exist"} |] { matchStatus = 404 - , matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=PGRST205" - , "Content-Length" <:> "182" ] + , matchHeaders = [ "Proxy-Status" <:> "PostgREST; error=42P01" + , "Content-Length" <:> "107" ] } it "works with Jwt error" $ do diff --git a/test/spec/Feature/Query/InsertSpec.hs b/test/spec/Feature/Query/InsertSpec.hs index 7aef6ce2b6..fada79160d 100644 --- a/test/spec/Feature/Query/InsertSpec.hs +++ b/test/spec/Feature/Query/InsertSpec.hs @@ -478,7 +478,7 @@ spec actualPgVersion = do {"id": 204, "body": "yyy"}, {"id": 205, "body": "zzz"}]|] `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |] + [json| {"code":"PGRST125","details":null,"hint":null,"message":"Invalid path specified in request URL"} |] { matchStatus = 404 , matchHeaders = [] } diff --git a/test/spec/Feature/Query/MultipleSchemaSpec.hs b/test/spec/Feature/Query/MultipleSchemaSpec.hs index 4a94c54194..8411cb6738 100644 --- a/test/spec/Feature/Query/MultipleSchemaSpec.hs +++ b/test/spec/Feature/Query/MultipleSchemaSpec.hs @@ -64,13 +64,7 @@ spec = } it "doesn't find another_table in schema v1" $ - request methodGet "/another_table" - [("Accept-Profile", "v1")] "" - `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":null,"message":"Could not find the table 'v1.another_table' in the schema cache"} |] - { matchStatus = 404 - , matchHeaders = [] - } + request methodGet "/another_table" [("Accept-Profile", "v1")] "" `shouldRespondWith` 404 it "fails trying to read table from unknown schema" $ request methodGet "/parents" [("Accept-Profile", "unknown")] "" `shouldRespondWith` diff --git a/test/spec/Feature/Query/QuerySpec.hs b/test/spec/Feature/Query/QuerySpec.hs index ca51eaca30..c4c3f02317 100644 --- a/test/spec/Feature/Query/QuerySpec.hs +++ b/test/spec/Feature/Query/QuerySpec.hs @@ -24,12 +24,7 @@ spec = do describe "Querying a nonexistent table" $ it "causes a 404" $ - get "/faketable" - `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.private_table'","message":"Could not find the table 'test.faketable' in the schema cache"} |] - { matchStatus = 404 - , matchHeaders = ["Content-Length" <:> "166"] - } + get "/faketable" `shouldRespondWith` 404 describe "Filtering response" $ do it "matches with equality" $ @@ -848,12 +843,14 @@ spec = do , matchHeaders = [matchContentTypeJson] } - -- we only search for foreign key relationships after checking the - -- the existence of first table, #3869 - it "table not found error if first table does not exist" $ + it "cannot request a partitioned table as parent from a partition" $ get "/car_model_sales_202101?select=id,name,car_models(id,name)&order=id.asc" `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.car_model_sales'","message":"Could not find the table 'test.car_model_sales_202101' in the schema cache"} |] - { matchStatus = 404 + [json| + {"hint":"Perhaps you meant 'car_model_sales' instead of 'car_model_sales_202101'.", + "details":"Searched for a foreign key relationship between 'car_model_sales_202101' and 'car_models' in the schema 'test', but no matches were found.", + "code":"PGRST200", + "message":"Could not find a relationship between 'car_model_sales_202101' and 'car_models' in the schema cache"} |] + { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } @@ -868,10 +865,14 @@ spec = do , matchHeaders = [matchContentTypeJson] } - it "table not found error if first table does not exist" $ + it "cannot request partitioned tables as children from a partition" $ get "/car_models_default?select=id,name,car_model_sales(id,name)&order=id.asc" `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.car_model_sales'","message":"Could not find the table 'test.car_models_default' in the schema cache"} |] - { matchStatus = 404 + [json| + {"hint":"Perhaps you meant 'car_model_sales' instead of 'car_models_default'.", + "details":"Searched for a foreign key relationship between 'car_models_default' and 'car_model_sales' in the schema 'test', but no matches were found.", + "code":"PGRST200", + "message":"Could not find a relationship between 'car_models_default' and 'car_model_sales' in the schema cache"} |] + { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } diff --git a/test/spec/Feature/Query/UpdateSpec.hs b/test/spec/Feature/Query/UpdateSpec.hs index ee18e32085..8109b66a3a 100644 --- a/test/spec/Feature/Query/UpdateSpec.hs +++ b/test/spec/Feature/Query/UpdateSpec.hs @@ -17,11 +17,7 @@ spec = do it "indicates no table found by returning 404" $ request methodPatch "/fake" [] [json| { "real": false } |] - `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.factories'","message":"Could not find the table 'test.fake' in the schema cache"} |] - { matchStatus = 404 - , matchHeaders = ["Content-Length" <:> "157"] - } + `shouldRespondWith` 404 context "on an empty table" $ @@ -363,9 +359,9 @@ spec = do {"id": 204, "body": "yyy"}, {"id": 205, "body": "zzz"}]|] `shouldRespondWith` - [json| {"code":"PGRST205","details":null,"hint":"Perhaps you meant the table 'test.articles'","message":"Could not find the table 'test.garlic' in the schema cache"} |] + [json| {"code":"PGRST125","details":null,"hint":null,"message":"Invalid path specified in request URL"} |] { matchStatus = 404 - , matchHeaders = ["Content-Length" <:> "158"] + , matchHeaders = [] } context "apply defaults on missing values" $ do