Skip to content

Commit a5e988f

Browse files
committed
refactored representation
1 parent 17bca51 commit a5e988f

2 files changed

Lines changed: 42 additions & 37 deletions

File tree

src/Database/PostgreSQL/Simple/Bind/Representation.hs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -56,64 +56,63 @@ data PGResult = PGSingle String
5656

5757
-- | Takes PostgreSQL function signature and represent it as an algebraic data type.
5858
parsePGFunction :: Text -> PGFunction
59-
parsePGFunction s = either error id (parseOnly parseFunction s) where
59+
parsePGFunction s = either error id (parseOnly (ss *> function) s) where
6060
ss = skipSpace
6161

62-
parseFunction = do
63-
_ <- ss *> asciiCI "function"
64-
schema <- ss *> ((parseIdentifier <* (char '.')) <|> (string ""))
65-
name <- ss *> parseIdentifier
66-
args <- ss *> char '(' *> (parseArgs `sepBy` (char ','))
67-
ret <- ss *> char ')' *> parseReturn
62+
function = do
63+
_ <- asciiCI "function"
64+
schema <- ss *> ((identifier <* (char '.')) <|> (string ""))
65+
name <- ss *> identifier
66+
args <- ss *> char '(' *> (arguments `sepBy` (char ','))
67+
ret <- ss *> char ')' *> returnType
68+
_ <- ss
6869
return $ PGFunction (unpack schema) (unpack name) args ret
6970

70-
parseArgs = do
71-
name <- ss *> parseIdentifier
72-
datatype <- ss *> parseType
71+
arguments = do
72+
name <- ss *> identifier
73+
datatype <- ss *> postgresType
7374
def <- ss *> ((asciiCI "default" <|> string "=") *> (takeTill (inClass ",)")) <|> (string ""))
7475
-- WARNING: parsing default values requires ability to parse almost arbitraty expressions.
7576
-- Here is a quick and dirty implementation of the parser.
7677

7778
return $ PGArgument (unpack name) (unpack datatype) ((length def) > 0)
7879

79-
parseCols = do
80-
name <- ss *> parseIdentifier
81-
datatype <- ss *> parseType <* ss
80+
cols = do
81+
name <- ss *> identifier
82+
datatype <- ss *> postgresType <* ss
8283
return $ PGColumn (unpack name) (unpack datatype)
8384

84-
parseReturn = do
85+
returnType = do
8586
ret <- ss *> asciiCI "returns" *> ss *> (
8687
asciiCI "setof"
8788
<|> asciiCI "table"
88-
<|> parseType)
89+
<|> postgresType)
8990

9091
case toLower(ret) of
91-
"setof" -> (PGSetOf . unpack) <$> (ss *> parseType)
92-
"table" -> PGTable <$> (ss *> char '(' *> parseCols `sepBy` (char ',') <* ss <* char ')')
92+
"setof" -> (PGSetOf . unpack) <$> (ss *> postgresType)
93+
"table" -> PGTable <$> (ss *> char '(' *> cols `sepBy` (char ',') <* ss <* char ')')
9394
t -> return $ PGSingle (unpack t)
9495

95-
parseIdentifier = do
96+
identifier = do
9697
s1 <- takeWhile1 (inClass "a-zA-Z_")
9798
s2 <- takeWhile (inClass "a-zA-Z_0-9$")
9899
return $ toLower $ s1 `append` s2
99100

100-
parseType = toLower <$> (foldr1 (<|>) $
101+
postgresType = toLower <$> (foldr1 (<|>) $
101102
(map asciiCI [ "double precision" ])
102-
++ (map (\t -> (asciiCI t <* ss <* (parseModifiers 1))) ["bit", "character varying"])
103-
++ (map (\t -> (asciiCI t <* ss <* (parseModifiers 2))) ["numeric", "decimal"])
104-
++ (map parseTime ["timestamp", "time"])
105-
++ [parseInterval, parseIdentifier <* (parseModifiers 4)])
106-
-- WARNING: user defined types can have more complex modifiers.
107-
-- The argument of parseModifiers might be a subject to change.
108-
109-
parseTime t = do
110-
base <- asciiCI t <* ss <* (parseModifiers 1)
103+
++ (map (\t -> (asciiCI t <* ss <* (modifiers $ Just 1))) ["bit", "character varying"])
104+
++ (map (\t -> (asciiCI t <* ss <* (modifiers $ Just 2))) ["numeric", "decimal"])
105+
++ (map timeType ["timestamp", "time"])
106+
++ [intervalType, identifier <* (modifiers Nothing)])
107+
108+
timeType t = do
109+
base <- asciiCI t <* ss <* (modifiers $ Just 1)
111110
tz <- ss *> ((asciiCI "with time zone") <|> (asciiCI "without time zone") <|> (string ""))
112111
return $ case tz of
113112
"" -> base
114113
_ -> base `append` " " `append` tz
115114

116-
parseInterval = do
115+
intervalType = do
117116
base <- (asciiCI "interval") <* ss
118117
fields <- foldr1 (<|>) $ map asciiCI [
119118
"year to month"
@@ -130,19 +129,22 @@ parsePGFunction s = either error id (parseOnly parseFunction s) where
130129
, "minute"
131130
, "second"
132131
, ""]
133-
_ <- ss *> (parseModifiers 1)
132+
_ <- ss *> (modifiers $ Just 1)
134133

135134
return $ case fields of
136135
"" -> base
137136
_ -> base `append` " " `append` fields
138137

139138

140-
parseModifiers n = ((char '(') *>(foldl (*>)
141-
(ss *> (decimal :: Parser Int) *> ss)
142-
(replicate (n - 1) (char ',' *> ss *> (decimal :: Parser Int) *> ss)))
143-
*> (char ')') *> (string ""))
144-
<|> (case n of
145-
1 -> (string "")
146-
_ -> (parseModifiers (n - 1)))
139+
modifiers limit = (char '(') *> exact <* (char ')') <|> less where
140+
exact = ($ limit) $ maybe
141+
((modifier `sepBy` (char ',')) *> (string ""))
142+
(\n -> foldl1 ((*>) . (*> ((char ',') *> ss))) (replicate n modifier) *> string "")
147143

144+
less = ($ limit) $ maybe
145+
(string "")
146+
(\n -> case n of
147+
1 -> (string "")
148+
_ -> (modifiers $ Just (n - 1)))
148149

150+
modifier = (decimal :: Parser Int) *> ss

tests/Main.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ spec = do
9494
(parsePGFunction "function f(x numeric(10,3)) returns void") `shouldBe`
9595
(PGFunction "" "f" [PGArgument "x" "numeric" False] (PGSingle "void"))
9696

97+
(parsePGFunction "function f(x user_defined_type(1,2,3,4)) returns void") `shouldBe`
98+
(PGFunction "" "f" [PGArgument "x" "user_defined_type" False] (PGSingle "void"))
99+
97100

98101
it "works for time types" $ do
99102
(parsePGFunction "function f(x time) returns void") `shouldBe`

0 commit comments

Comments
 (0)