packages feed

postgresql-placeholder-converter (empty) → 0.1.0.0

raw patch · 9 files changed

+296/−0 lines, 9 filesdep +attoparsecdep +basedep +bytestringsetup-changed

Dependencies added: attoparsec, base, bytestring, hspec, mtl, postgresql-placeholder-converter, utf8-string

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history++## 0.1.0.0 -- 2020-06-29++- Released.
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2019, IIJ Innovation Institute Inc.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++  * Redistributions of source code must retain the above copyright+    notice, this list of conditions and the following disclaimer.+  * Redistributions in binary form must reproduce the above copyright+    notice, this list of conditions and the following disclaimer in+    the documentation and/or other materials provided with the+    distribution.+  * Neither the name of the copyright holders nor the names of its+    contributors may be used to endorse or promote products derived+    from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,5 @@+# postgresql-placeholder-converter++[![GitHub Actions](https://github.com/kakkun61/postgresql-placeholder-converter/workflows/test/badge.svg?branch=master)](https://github.com/kakkun61/postgresql-placeholder-converter/actions?query=workflow%3Atest)++[![Hackage](https://matrix.hackage.haskell.org/api/v2/packages/postgresql-placeholder-converter/badge)](http://hackage.haskell.org/package/postgresql-placeholder-converter)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ postgresql-placeholder-converter.cabal view
@@ -0,0 +1,58 @@+cabal-version:       2.2++name:                postgresql-placeholder-converter+version:             0.1.0.0+author:              Kazuki Okamoto+copyright:           2020 Kazuki Okamoto, 2019 IIJ Innovation Institute Inc.+license:             BSD-3-Clause+license-file:        LICENSE+maintainer:          kazuki.okamoto@kakkun61.com+category:            Database+description:         This library provides functions to convert question mark style to dollar sign style of PostgreSQL SQL.+synopsis:            Converter for question mark style and dollar sign style of PostgreSQL SQL.+homepage:            https://github.com/kakkun61/postgresql-placeholder-converter+bug-reports:         https://github.com/kakkun61/postgresql-placeholder-converter/issues++build-type:          Simple++extra-source-files:  README.md,+                     CHANGELOG.md++common common+  build-depends:       base >= 4.12 && < 4.15+  default-language:    Haskell2010++library+  import:              common+  hs-source-dirs:      src+  exposed-modules:     Database.PostgreSQL.Placeholder.Convert+  other-modules:       Database.PostgreSQL.Placeholder.Convert.QuestionToDollar+  build-depends:       attoparsec,+                       bytestring,+                       mtl,+                       utf8-string+  ghc-options:         -Wall+                       -Wcompat+                       -Wincomplete-uni-patterns+                       -Wincomplete-record-updates+                       -Wmonomorphism-restriction+                       -Wmissing-exported-signatures+                       -Wmissing-home-modules+                       -Wmissing-import-lists+                       -Widentities+                       -Wredundant-constraints+                       -Wno-name-shadowing++test-suite test+  import:             common+  type:               exitcode-stdio-1.0+  hs-source-dirs:     test+  main-is:            Spec.hs+  other-modules:      Database.PostgreSQL.Placeholder.ConvertSpec+  ghc-options:        -threaded+                      -rtsopts+                      -with-rtsopts=-N+                      -Wno-missing-import-lists+  build-depends:      postgresql-placeholder-converter,+                      hspec+  build-tool-depends: hspec-discover:hspec-discover
+ src/Database/PostgreSQL/Placeholder/Convert.hs view
@@ -0,0 +1,11 @@+module Database.PostgreSQL.Placeholder.Convert+  ( convertQuestionMarkStyleToDollarSignStyle+  , QD.splitQueries+  ) where++import qualified Database.PostgreSQL.Placeholder.Convert.QuestionToDollar as QD++import Data.ByteString (ByteString)++convertQuestionMarkStyleToDollarSignStyle :: ByteString-> Either String ByteString+convertQuestionMarkStyleToDollarSignStyle = QD.convert
+ src/Database/PostgreSQL/Placeholder/Convert/QuestionToDollar.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE OverloadedStrings #-}++module Database.PostgreSQL.Placeholder.Convert.QuestionToDollar+  ( convert+  , splitQueries+  ) where++import           Control.Monad                    (void)+import           Control.Monad.State.Strict       (StateT, evalStateT, lift, state)+import qualified Data.Attoparsec.ByteString       as AP+import qualified Data.Attoparsec.ByteString.Char8 as APC+import           Data.Attoparsec.Combinator       ((<?>))+import qualified Data.Attoparsec.Combinator       as AP+import qualified Data.Attoparsec.Internal.Types   as API+import           Data.ByteString                  (ByteString)+import qualified Data.ByteString                  as BS+import qualified Data.ByteString.UTF8             as BSU++-- | Convert question mark style to dollar sign style of PostgreSQL SQL.+convert :: ByteString-> Either String ByteString+convert q =+  flip AP.parseOnly q $+    flip evalStateT 1 $+      (mconcat <$>) $ do+        ss <-+          AP.many' $+            AP.choice+              [ question+              , lift questionLiteral+              , lift singleQuoteLiteral+              , lift doubleQuoteLiteral+              , lift dollarQuoteLiteral+              , lift lineComment+              , lift blockComment+              , lift $ BS.pack . (:[]) <$> AP.anyWord8+              ]+        lift AP.endOfInput+        pure ss++singleQuoteLiteral :: AP.Parser BS.ByteString+singleQuoteLiteral = oneCharQuoteLiteral '\'' <?> "singleQuoteLiteral"++doubleQuoteLiteral :: AP.Parser BS.ByteString+doubleQuoteLiteral = oneCharQuoteLiteral '"' <?> "doubleQuoteLiteral"++oneCharQuoteLiteral :: Char -> AP.Parser BS.ByteString+oneCharQuoteLiteral quote =+  takeConsumed $ do+    void $ APC.char quote+    body+    void $ APC.char quote+  where+    body = do+      void $ APC.takeTill ((||) <$> (== '\\') <*> (== quote))+      AP.option () $ do+        void $ APC.char '\\'+        void APC.anyChar -- It continues although it is something wrong if the character does not stand for escape sequence.+        body++dollarQuoteLiteral :: AP.Parser BS.ByteString+dollarQuoteLiteral =+  (<?> "dollarQuoteLiteral") $+    takeConsumed $ do+      quote <- dollarQuote+      go quote+  where+    dollarQuote =+      (<?> "dollarQuote") $+        takeConsumed $ do+          void $ APC.char '$'+          void $ AP.many' $ APC.satisfy ((&&) <$> (/= ' ') <*> (/= '$'))+          void $ APC.char '$'+    go quote = go'+      where+        go' = do+          void $ AP.many' $ APC.satisfy (/= '$')+          AP.choice+            [ void $ AP.string quote+            , APC.char '$' *> go'+            ]++lineComment :: AP.Parser BS.ByteString+lineComment =+  (<?> "lineComment") $+    takeConsumed $ do+      void "--"+      void $ AP.many' $ APC.satisfy ((&&) <$> (/= '\r') <*> (/= '\n'))+      AP.option () $ void APC.endOfLine++blockComment :: AP.Parser BS.ByteString+blockComment =+  (<?> "blockComment") $+    takeConsumed $ do+      void "/*"+      go+  where+    go = do+      void $+        AP.many' $+          AP.choice+            [ void blockComment+            , void $ APC.satisfy ((&&) <$> (/= '/') <*> (/= '*'))+            ]+      AP.choice+        [ void "*/"+        , AP.lookAhead "/*" *> go+        , APC.satisfy ((||) <$> (== '/') <*> (== '*')) *> go+        ]++question :: StateT Int AP.Parser BS.ByteString+question = do+  void $ lift $ APC.char '?'+  i <- getAndIncrement+  pure $ "$" <> BSU.fromString (show i)+  where+    getAndIncrement :: StateT Int AP.Parser Int+    getAndIncrement = state (\i -> (i, i + 1))++questionLiteral :: AP.Parser BS.ByteString+questionLiteral = takeConsumed "\\?"++-- | Split a query including multiple queries separated by semicolon.+splitQueries :: ByteString -> [ByteString]+splitQueries q =+  case AP.parseOnly parser q of+    Right qs -> qs+    Left _   -> [q]+  where+    parser =+      (conv <$>) $+        AP.many' $+          AP.choice+            [ APC.char ';' >> pure Nothing+            , Just <$> singleQuoteLiteral+            , Just <$> doubleQuoteLiteral+            , Just <$> dollarQuoteLiteral+            , Just <$> lineComment+            , Just <$> blockComment+            , Just . BS.pack . (:[]) <$> AP.anyWord8+            ]+    conv :: [Maybe BS.ByteString] -> [BS.ByteString]+    conv =+      foldr go []+      where+        go (Just s) (a:as) = (s <> a):as+        go (Just s) []     = [s]+        go Nothing acc     = "":acc++takeConsumed :: AP.Parser a -> AP.Parser BS.ByteString+takeConsumed p = do+  n <-+    AP.lookAhead $ do+      API.Pos startPos <- currentPos+      void p+      API.Pos endPos <- currentPos+      pure $ endPos - startPos+  AP.take n++currentPos :: AP.Parser API.Pos+currentPos = API.Parser $ \t pos more _lose suc -> suc t pos more pos
+ test/Database/PostgreSQL/Placeholder/ConvertSpec.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE OverloadedStrings #-}++module Database.PostgreSQL.Placeholder.ConvertSpec (spec) where++import Database.PostgreSQL.Placeholder.Convert++import Test.Hspec++spec :: Spec+spec = do+  describe "convertQuestionMarkStyleToDollarSignStyle" $ do+    it "SELECT * FROM person" $ do+      convertQuestionMarkStyleToDollarSignStyle "SELECT * FROM person" `shouldBe` Right "SELECT * FROM person"++    it "SELECT * FROM person WHERE id = ?" $ do+      convertQuestionMarkStyleToDollarSignStyle "SELECT * FROM person WHERE id = ?" `shouldBe` Right "SELECT * FROM person WHERE id = $1"++    it "SELECT * FROM person WHERE name = '?'" $ do+      convertQuestionMarkStyleToDollarSignStyle "SELECT * FROM person WHERE name = '?'" `shouldBe` Right "SELECT * FROM person WHERE name = '?'"++    it "SELECT * FROM person WHERE name = $$?$$" $ do+      convertQuestionMarkStyleToDollarSignStyle "SELECT * FROM person WHERE name = $$?$$" `shouldBe` Right "SELECT * FROM person WHERE name = $$?$$"++    it "SELECT * FROM person WHERE name = $name$?$name$" $ do+      convertQuestionMarkStyleToDollarSignStyle "SELECT * FROM person WHERE name = $name$?$name$" `shouldBe` Right "SELECT * FROM person WHERE name = $name$?$name$"
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}