postgresql-syntax 0.4.1.3 → 0.4.2
raw patch · 4 files changed
+68/−3 lines, 4 files
Files
- library/PostgresqlSyntax/Extras/HeadedMegaparsec.hs +13/−1
- library/PostgresqlSyntax/Parsing.hs +21/−1
- postgresql-syntax.cabal +1/−1
- tasty-test/Main.hs +33/−0
library/PostgresqlSyntax/Extras/HeadedMegaparsec.hs view
@@ -20,7 +20,19 @@ -- * Executors run :: (Ord err, VisualStream strm, TraversableStream strm, Megaparsec.ShowErrorComponent err) => HeadedParsec err strm a -> strm -> Either String a-run p = first Megaparsec.errorBundlePretty . Megaparsec.runParser (toParsec p <* Megaparsec.eof) ""+run p = first Megaparsec.errorBundlePretty . runParser p++-- |+-- Run the parser but returns all the error messages separated, along with their offset in the parsed message.+runParserWithErrorPos :: (Show (Megaparsec.Token strm), Show e, Ord e, VisualStream strm, TraversableStream strm, Megaparsec.ShowErrorComponent e) => HeadedParsec e strm a -> strm -> Either (NonEmpty (Int, String)) a+runParserWithErrorPos p s = case runParser p s of+ Left err -> Left (extractor <$> Megaparsec.bundleErrors err)+ Right v -> Right v+ where+ extractor x = (Megaparsec.errorOffset x, Megaparsec.parseErrorPretty x)++runParser :: (Ord e, VisualStream strm, TraversableStream strm) => HeadedParsec e strm a -> strm -> Either (Megaparsec.ParseErrorBundle strm e) a+runParser p = Megaparsec.runParser (toParsec p <* Megaparsec.eof) "" -- * Primitives
library/PostgresqlSyntax/Parsing.hs view
@@ -54,6 +54,9 @@ run :: Parser a -> Text -> Either String a run = Extras.run +runWithPosError :: Parser a -> Text -> Either (NonEmpty (Int, String)) a+runWithPosError = Extras.runParserWithErrorPos+ -- * Helpers commaSeparator :: Parser ()@@ -115,6 +118,23 @@ in collectChunks mempty return tail +-- |+-- >>> testParser dollarQuotedSconst "$$it's good$$"+-- "it's good"+dollarQuotedSconst :: Parser Text+dollarQuotedSconst = do+ char '$'+ quoteTag <- takeWhileP Nothing (/= '$')+ let terminator = Megaparsec.chunk $ "$" <> quoteTag <> "$"+ char '$'+ endHead+ tail <-+ parse $ do+ body <- many $ Megaparsec.notFollowedBy terminator *> Megaparsec.anySingle+ terminator+ return $ Text.pack body+ return tail+ atEnd :: Parser a -> Parser a atEnd p = space *> p <* endHead <* space <* eof @@ -1552,7 +1572,7 @@ fconst = float -sconst = quotedString '\''+sconst = quotedString '\'' <|> dollarQuotedSconst constTypename = asum
postgresql-syntax.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: postgresql-syntax-version: 0.4.1.3+version: 0.4.2 category: Database, PostgreSQL, Parsing synopsis: PostgreSQL AST parsing and rendering description:
tasty-test/Main.hs view
@@ -1,5 +1,6 @@ module Main where +import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Text as Text import qualified PostgresqlSyntax.Parsing as Parsing import Test.Tasty@@ -56,6 +57,38 @@ "inet", "json", "jsonb"+ ],+ testParserOnAllInputs+ "sconst"+ Parsing.sconst+ [ "'it''s good'",+ "$$it's good$$",+ "$x$it's good$x$" ]+ ],+ testGroup "Error reporting"+ $ let testParserOnAllInputs parserName parser inputs res =+ testCase parserName+ $ forM_ inputs+ $ \input -> case Parsing.runWithPosError parser input of+ Left err -> show (NonEmpty.head err) @?= res+ Right _ -> return ()+ in [ testParserOnAllInputs+ "Typo in FROM keyword"+ Parsing.preparableStmt+ [ "select i :: int8 fom auth.user as u\n\+ \inner join edgenode.usere_provider as p\n\+ \on u.id = p.user_id\n\+ \inner join edgenode.provider_branch as b\n\+ \on b.provider_fk = p.provider_id"+ ]+ "(20,\"offset=20:\\nunexpected space\\nexpecting end of input\\n\")",+ testParserOnAllInputs+ "Typo in NOT keyword"+ Parsing.preparableStmt+ [ "select i :: int8 from auth.user as u\n\+ \WHERE u.id IS NO NULL && TRUE"+ ]+ "(53,\"offset=53:\\nexpecting white space\\n\")" ] ]