diff --git a/CHANGELOG.md b/CHANGELOG.md index e846d5715..59226ef0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #1205, Add support for parsing JSON Web Key Sets - @russelldavies - #1203, Add support for reading db-uri from a separate file - @zhoufeng1989 - #1200, Add db-extra-search-path config for adding schemas to the search_path, solves issues related to extensions created on the public schema - @steve-chavez +- #1219, Add ability to quote column names on filters - @steve-chavez ### Fixed diff --git a/src/PostgREST/Parsers.hs b/src/PostgREST/Parsers.hs index 85a2a6931..a90689183 100644 --- a/src/PostgREST/Parsers.hs +++ b/src/PostgREST/Parsers.hs @@ -73,8 +73,8 @@ pStar = toS <$> (string "*" $> ("*"::ByteString)) pFieldName :: Parser Text pFieldName = - intercalate "-" . map toS <$> - (many1 (letter <|> digit <|> oneOf "_ ") `sepBy1` dash) + pQuotedValue <|> + intercalate "-" . map toS <$> (many1 (letter <|> digit <|> oneOf "_ ") `sepBy1` dash) "field name (* or [a..z0..9_])" where isDash :: GenParser Char st () @@ -153,10 +153,10 @@ pListVal :: Parser ListVal pListVal = lexeme (char '(') *> pListElement `sepBy1` char ',' <* lexeme (char ')') pListElement :: Parser Text -pListElement = try pQuotedValue <|> (toS <$> many (noneOf ",)")) +pListElement = try (pQuotedValue <* notFollowedBy (noneOf ",)")) <|> (toS <$> many (noneOf ",)")) pQuotedValue :: Parser Text -pQuotedValue = toS <$> (char '"' *> many (noneOf "\"") <* char '"' <* notFollowedBy (noneOf ",)")) +pQuotedValue = toS <$> (char '"' *> many (noneOf "\"") <* char '"') pDelimiter :: Parser Char pDelimiter = char '.' "delimiter (.)" @@ -195,7 +195,7 @@ pLogicTree = Stmnt <$> try pLogicFilter "logic operator (and, or)" pLogicSingleVal :: Parser SingleVal -pLogicSingleVal = try pQuotedValue <|> try pPgArray <|> (toS <$> many (noneOf ",)")) +pLogicSingleVal = try (pQuotedValue <* notFollowedBy (noneOf ",)")) <|> try pPgArray <|> (toS <$> many (noneOf ",)")) where pPgArray :: Parser Text pPgArray = do @@ -236,9 +236,7 @@ pJSPath = toJSPath <$> (period *> pPath `sepBy` period <* eof) pPath = (,) <$> pJSPKey <*> optionMaybe pJSPIdx pJSPKey :: Parser Text -pJSPKey = toS <$> (many1 (alphaNum <|> oneOf "_$@") <|> pQuoted) "attribute name [a..z0..9_$@])" - where - pQuoted = char '"' *> many (noneOf "\"") <* char '"' +pJSPKey = toS <$> many1 (alphaNum <|> oneOf "_$@") <|> pQuotedValue "attribute name [a..z0..9_$@])" pJSPIdx :: Parser Int pJSPIdx = char '[' *> (read <$> many1 digit) <* char ']' "array index [0..n]" diff --git a/test/Feature/QuerySpec.hs b/test/Feature/QuerySpec.hs index 058b15282..6b52d086e 100644 --- a/test/Feature/QuerySpec.hs +++ b/test/Feature/QuerySpec.hs @@ -426,7 +426,7 @@ spec = do { matchHeaders = [matchContentTypeJson] } it "can embed a view that has group by" $ - get "/projects_count_grouped_by?select=number_of_projects,client(name)" `shouldRespondWith` + get "/projects_count_grouped_by?select=number_of_projects,client(name)&order=number_of_projects" `shouldRespondWith` [json| [{"number_of_projects":1,"client":null}, {"number_of_projects":2,"client":{"name":"Microsoft"}}, @@ -828,6 +828,11 @@ spec = do {"Just A Server Model":" IBM,9133-55A (P5-55A)"}]|] { matchHeaders = [matchContentTypeJson] } + it "will select and filter a quoted column that has PostgREST reserved characters" $ + get "/pgrst_reserved_chars?select=%22:arr-%3Eow::cast%22,%22(inside,parens)%22,%22a.dotted.column%22,%22%20%20col%20%20w%20%20space%20%20%22&%22*id*%22=eq.1" `shouldRespondWith` + [json|[{":arr->ow::cast":" arrow-1 ","(inside,parens)":" parens-1 ","a.dotted.column":" dotted-1 "," col w space ":" space-1"}]|] + { matchHeaders = [matchContentTypeJson] } + describe "binary output" $ do context "on GET" $ do it "can query if a single column is selected" $ diff --git a/test/fixtures/data.sql b/test/fixtures/data.sql index 1f7b7a815..94dfc4df9 100644 --- a/test/fixtures/data.sql +++ b/test/fixtures/data.sql @@ -476,9 +476,16 @@ INSERT INTO isn_sample VALUES ('978-0-393-04002-9', 'Mathematics: From the Birth TRUNCATE TABLE "Server Today" CASCADE; COPY "Server Today" ("cHostname", "Just A Server Model") FROM STDIN CSV DELIMITER '|'; -argnim1 | IBM,9113-550 (P5-550) -argnim2 | IBM,9113-550 (P5-550) +argnim1 | IBM,9113-550 (P5-550) +argnim2 | IBM,9113-550 (P5-550) daaa2nim71 | IBM,9131-52A (P5-52A) daah3nim71 | IBM,8406-71Y (P7-PS701) -hbnim1 | IBM,9133-55A (P5-55A) +hbnim1 | IBM,9133-55A (P5-55A) +\. + +TRUNCATE TABLE pgrst_reserved_chars CASCADE; +COPY pgrst_reserved_chars ("*id*", ":arr->ow::cast", "(inside,parens)", "a.dotted.column", " col w space ") FROM STDIN CSV DELIMITER '|'; +1 | arrow-1 | parens-1 | dotted-1 | space-1 +2 | arrow-2 | parens-2 | dotted-2 | space-2 +3 | arrow-3 | parens-3 | dotted-3 | space-3 \. diff --git a/test/fixtures/privileges.sql b/test/fixtures/privileges.sql index fd9203a42..5c12c5ed3 100644 --- a/test/fixtures/privileges.sql +++ b/test/fixtures/privileges.sql @@ -97,6 +97,7 @@ GRANT ALL ON TABLE , isn_sample , projects_count_grouped_by , "Server Today" + , pgrst_reserved_chars TO postgrest_test_anonymous; GRANT INSERT ON TABLE insertonly TO postgrest_test_anonymous; diff --git a/test/fixtures/schema.sql b/test/fixtures/schema.sql index 8e888d16f..2f9f22e1d 100755 --- a/test/fixtures/schema.sql +++ b/test/fixtures/schema.sql @@ -1641,3 +1641,11 @@ create table "Server Today"( "cHostname" text, "Just A Server Model" text ); + +create table test.pgrst_reserved_chars ( + "*id*" integer, + ":arr->ow::cast" text, + "(inside,parens)" text, + "a.dotted.column" text, + " col w space " text +);