From 40c2bcd4a1f63b9d639574a07202a1edf9edc4b0 Mon Sep 17 00:00:00 2001 From: Laurence Isla Date: Mon, 31 Jul 2023 18:36:37 -0500 Subject: [PATCH] fix: paramaters of type character and bit not ignoring length - Fixes the error "value too long for type character(1)" when the char length of the parameter was bigger than one. --- CHANGELOG.md | 4 ++- src/PostgREST/Query/QueryBuilder.hs | 2 +- src/PostgREST/Response/OpenAPI.hs | 4 +-- src/PostgREST/SchemaCache.hs | 8 ++++++ src/PostgREST/SchemaCache/Routine.hs | 9 ++++--- test/spec/Feature/Query/RpcSpec.hs | 22 +++++++++++++++++ test/spec/Feature/Query/UpdateSpec.hs | 4 +-- test/spec/fixtures/schema.sql | 35 ++++++++++++++++++++++----- 8 files changed, 72 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fe8be0aa..75dc0e760 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #2594, Fix unused index on jsonb/jsonb arrow filter and order (``/bets?data->>contractId=eq.1`` and ``/bets?order=data->>contractId``) - @steve-chavez - #2861, Fix character and bit columns with fixed length not inserting/updating properly - @laurenceisla + Fixes the error "value too long for type character(1)" when the char length of the column was bigger than one. - - #2862, Fix null filtering on embedded resource when using a column name equal to the relation name - @steve-chave + - #2862, Fix null filtering on embedded resource when using a column name equal to the relation name - @steve-chavez + - #1586, Fix function parameters of type character and bit not ignoring length - @laurenceisla + + Fixes the error "value too long for type character(1)" when the char length of the parameter was bigger than one. ## [11.1.0] - 2023-06-07 diff --git a/src/PostgREST/Query/QueryBuilder.hs b/src/PostgREST/Query/QueryBuilder.hs index c3b92bc17..cff4c44f0 100644 --- a/src/PostgREST/Query/QueryBuilder.hs +++ b/src/PostgREST/Query/QueryBuilder.hs @@ -171,7 +171,7 @@ callPlanToQuery (FunctionCall qi params args returnsScalar returnsSetOfScalar re fromCall = case params of OnePosParam prm -> "FROM " <> callIt (singleParameter args $ encodeUtf8 $ ppType prm) KeyParams [] -> "FROM " <> callIt mempty - KeyParams prms -> fromJsonBodyF args ((\p -> CoercibleField (ppName p) mempty False (ppType p) Nothing Nothing) <$> prms) False True False <> ", " <> + KeyParams prms -> fromJsonBodyF args ((\p -> CoercibleField (ppName p) mempty False (ppTypeMaxLength p) Nothing Nothing) <$> prms) False True False <> ", " <> "LATERAL " <> callIt (fmtParams prms) callIt :: SQL.Snippet -> SQL.Snippet diff --git a/src/PostgREST/Response/OpenAPI.hs b/src/PostgREST/Response/OpenAPI.hs index db855f9fd..b1391247d 100644 --- a/src/PostgREST/Response/OpenAPI.hs +++ b/src/PostgREST/Response/OpenAPI.hs @@ -154,7 +154,7 @@ makeProcSchema pd = & required .~ fmap ppName (filter ppReq (pdParams pd)) makeProcProperty :: RoutineParam -> (Text, Referenced Schema) -makeProcProperty (RoutineParam n t _ _) = (n, Inline s) +makeProcProperty (RoutineParam n t _ _ _) = (n, Inline s) where s = (mempty :: Schema) & type_ .~ toSwaggerType t @@ -181,7 +181,7 @@ makePreferParam ts = _ -> [] makeProcGetParam :: RoutineParam -> Referenced Param -makeProcGetParam (RoutineParam n t r v) = +makeProcGetParam (RoutineParam n t _ r v) = Inline $ (mempty :: Param) & name .~ n & required ?~ r diff --git a/src/PostgREST/SchemaCache.hs b/src/PostgREST/SchemaCache.hs index b56549f8a..214fa7f06 100644 --- a/src/PostgREST/SchemaCache.hs +++ b/src/PostgREST/SchemaCache.hs @@ -259,6 +259,7 @@ decodeFuncs = (RoutineParam <$> compositeField HD.text <*> compositeField HD.text + <*> compositeField HD.text <*> compositeField HD.bool <*> compositeField HD.bool) <*> (parseRetType @@ -367,6 +368,13 @@ funcsSqlQuery pgVer = [q| array_agg(( COALESCE(name, ''), -- name type::regtype::text, -- type + CASE type + WHEN 'bit'::regtype THEN 'bit varying' + WHEN 'bit[]'::regtype THEN 'bit varying[]' + WHEN 'character'::regtype THEN 'character varying' + WHEN 'character[]'::regtype THEN 'character varying[]' + ELSE type::regtype::text + END, -- convert types that ignore the lenth and accept any value till maximum size idx <= (pronargs - pronargdefaults), -- is_required COALESCE(mode = 'v', FALSE) -- is_variadic ) ORDER BY idx) AS args, diff --git a/src/PostgREST/SchemaCache/Routine.hs b/src/PostgREST/SchemaCache/Routine.hs index c25fe47fd..0fed645c1 100644 --- a/src/PostgREST/SchemaCache/Routine.hs +++ b/src/PostgREST/SchemaCache/Routine.hs @@ -69,10 +69,11 @@ instance JSON.ToJSON Routine where ] data RoutineParam = RoutineParam - { ppName :: Text - , ppType :: Text - , ppReq :: Bool - , ppVar :: Bool + { ppName :: Text + , ppType :: Text + , ppTypeMaxLength :: Text + , ppReq :: Bool + , ppVar :: Bool } deriving (Eq, Show, Ord, Generic, JSON.ToJSON) diff --git a/test/spec/Feature/Query/RpcSpec.hs b/test/spec/Feature/Query/RpcSpec.hs index 9b99f421e..56e28165d 100644 --- a/test/spec/Feature/Query/RpcSpec.hs +++ b/test/spec/Feature/Query/RpcSpec.hs @@ -1447,3 +1447,25 @@ spec actualPgVersion = { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } + + -- https://github.com/PostgREST/postgrest/issues/1586#issuecomment-696345442 + context "a proc with bit and char parameters" $ do + it "modifies the param type from character to character varying" $ do + get "/rpc/char_param_select?char_=abcdefg&char_arr={abc,abcdefg}" `shouldRespondWith` + [json| [{ "char_": "abcdefg", "char_arr": [ "abc", "abcdefg" ] }] |] + { matchHeaders = [matchContentTypeJson] } + + post "/rpc/char_param_insert" [json| { "char_": "abcdefg", "char_arr": "{abc,abcdefg}" } |] + `shouldRespondWith` + [json| {"code":"22001","details":null,"hint":null,"message":"value too long for type character(5)"} |] + { matchStatus = 400 } + + it "modifies the param type from bit to bit varying" $ do + get "/rpc/bit_param_select?bit_=101010&bit_arr={101,101010}" `shouldRespondWith` + [json| [{ "bit_": "101010", "bit_arr": [ "101", "101010" ] }] |] + { matchHeaders = [matchContentTypeJson] } + + post "/rpc/bit_param_insert" [json| { "bit_": "101010", "bit_arr": "{101,101010}" } |] + `shouldRespondWith` + [json| {"code":"22026","details":null,"hint":null,"message":"bit string length 6 does not match type bit(5)"} |] + { matchStatus = 400 } diff --git a/test/spec/Feature/Query/UpdateSpec.hs b/test/spec/Feature/Query/UpdateSpec.hs index 523a8873b..424e8bcd9 100644 --- a/test/spec/Feature/Query/UpdateSpec.hs +++ b/test/spec/Feature/Query/UpdateSpec.hs @@ -387,14 +387,14 @@ spec actualPgVersion = do -- https://github.com/PostgREST/postgrest/issues/2861 context "bit and char columns with length" $ do it "should update a bit column with length" $ - request methodPatch "/bitchar_with_length?char=eq.aaaaa" + request methodPatch "/bitchar_with_length?select=bit,char&char=eq.aaaaa" [("Prefer", "return=representation")] [json|{"bit": "11100"}|] `shouldRespondWith` [json|[{ "bit": "11100", "char": "aaaaa" }]|] { matchStatus = 200 } it "should update a char column with length" $ - request methodPatch "/bitchar_with_length?bit=eq.00000" + request methodPatch "/bitchar_with_length?select=bit,char&bit=eq.00000" [("Prefer", "return=representation")] [json|{"char": "zzzyy"}|] `shouldRespondWith` [json|[{ "bit": "00000", "char": "zzzyy" }]|] diff --git a/test/spec/fixtures/schema.sql b/test/spec/fixtures/schema.sql index 8b7315399..5fb2ecd74 100644 --- a/test/spec/fixtures/schema.sql +++ b/test/spec/fixtures/schema.sql @@ -3303,12 +3303,6 @@ create table bets ( create index bets_data_json on bets ((data_json ->>'contractId')); create index bets_data_jsonb on bets ((data_jsonb ->>'contractId')); --- https://github.com/PostgREST/postgrest/issues/2861 -CREATE TABLE bitchar_with_length ( - bit bit(5), - char char(5) -); - --- https://github.com/PostgREST/postgrest/issues/2862 create table profiles ( id uuid primary key, @@ -3329,3 +3323,32 @@ create table tournaments( id bigint primary key, status bigint references status(id) ); + +-- https://github.com/PostgREST/postgrest/issues/2861 +CREATE TABLE bitchar_with_length ( + bit bit(5), + char char(5), + bit_arr bit(5)[], + char_arr char(5)[] +); + +-- https://github.com/PostgREST/postgrest/issues/1586 +create or replace function char_param_select(char_ char(4), char_arr char(4)[]) +returns table(char_ char, char_arr char[]) as $$ + select $1, $2; +$$ language sql; + +create or replace function bit_param_select(bit_ char(4), bit_arr char(4)[]) +returns table(bit_ char, bit_arr char[]) as $$ + select $1, $2; +$$ language sql; + +create or replace function char_param_insert(char_ char(4), char_arr char(4)[]) +returns void as $$ + insert into bitchar_with_length(char, char_arr) values($1, $2); +$$ language sql; + +create or replace function bit_param_insert(bit_ bit(4), bit_arr bit(4)[]) +returns void as $$ +insert into bitchar_with_length(bit, bit_arr) values($1, $2); +$$ language sql;