9
9
module PostgREST.ApiRequest.QueryParams
10
10
( parse
11
11
, QueryParams (.. )
12
- , pRequestRange
12
+ , pTreePath
13
13
) where
14
14
15
15
import qualified Data.ByteString.Char8 as BS
16
- import qualified Data.HashMap.Strict as HM
17
16
import qualified Data.List as L
18
17
import qualified Data.Set as S
19
18
import qualified Data.Text as T
@@ -22,42 +21,42 @@ import qualified Network.HTTP.Base as HTTP
22
21
import qualified Network.HTTP.Types.URI as HTTP
23
22
import qualified Text.ParserCombinators.Parsec as P
24
23
25
- import Control.Arrow ((***) )
26
- import Data.Either.Combinators (mapLeft )
27
- import Data.List (init , last )
28
- import Data.Ranged.Boundaries (Boundary (.. ))
29
- import Data.Ranged.Ranges (Range (.. ))
30
- import Data.Tree (Tree (.. ))
31
- import Text.Parsec.Error (errorMessages ,
32
- showErrorMessages )
33
- import Text.ParserCombinators.Parsec (GenParser , ParseError , Parser ,
34
- anyChar , between , char , choice ,
35
- digit , eof , errorPos , letter ,
36
- lookAhead , many1 , noneOf ,
37
- notFollowedBy , oneOf ,
38
- optionMaybe , sepBy , sepBy1 ,
39
- string , try , (<?>) )
40
-
41
- import PostgREST.RangeQuery (NonnegRange , allRange ,
42
- rangeGeq , rangeLimit ,
43
- rangeOffset , restrictRange )
24
+ import Control.Arrow ((***) )
25
+ import Data.Either.Combinators (mapLeft )
26
+ import Data.List (init , last )
27
+ import Data.Tree (Tree (.. ))
28
+ import PostgREST.ApiRequest.Types (AggregateFunction (.. ),
29
+ EmbedParam (.. ), EmbedPath ,
30
+ Field , Filter (.. ),
31
+ FtsOperator (.. ), Hint ,
32
+ JoinType (.. ),
33
+ JsonOperand (.. ),
34
+ JsonOperation (.. ),
35
+ JsonPath , ListVal ,
36
+ LogicOperator (.. ),
37
+ LogicTree (.. ), OpExpr (.. ),
38
+ OpQuantifier (.. ),
39
+ Operation (.. ),
40
+ OrderDirection (.. ),
41
+ OrderNulls (.. ),
42
+ OrderTerm (.. ),
43
+ QPError (.. ),
44
+ QuantOperator (.. ),
45
+ SelectItem (.. ),
46
+ SimpleOperator (.. ),
47
+ SingleVal , TrileanVal (.. ))
44
48
import PostgREST.SchemaCache.Identifiers (FieldName )
45
-
46
- import PostgREST.ApiRequest.Types (AggregateFunction (.. ),
47
- EmbedParam (.. ), EmbedPath , Field ,
48
- Filter (.. ), FtsOperator (.. ),
49
- Hint , JoinType (.. ),
50
- JsonOperand (.. ),
51
- JsonOperation (.. ), JsonPath ,
52
- ListVal , LogicOperator (.. ),
53
- LogicTree (.. ), OpExpr (.. ),
54
- OpQuantifier (.. ), Operation (.. ),
55
- OrderDirection (.. ),
56
- OrderNulls (.. ), OrderTerm (.. ),
57
- QPError (.. ), QuantOperator (.. ),
58
- SelectItem (.. ),
59
- SimpleOperator (.. ), SingleVal ,
60
- TrileanVal (.. ))
49
+ import Text.Parsec.Error (errorMessages ,
50
+ showErrorMessages )
51
+ import Text.ParserCombinators.Parsec (GenParser , ParseError ,
52
+ Parser , anyChar , between ,
53
+ char , choice , digit , eof ,
54
+ errorPos , letter , lookAhead ,
55
+ many1 , noneOf ,
56
+ notFollowedBy , oneOf ,
57
+ optionMaybe , sepBy , sepBy1 ,
58
+ string , try , (<?>) )
59
+ import Text.Read (read )
61
60
62
61
import Protolude hiding (Sum , try )
63
62
@@ -67,8 +66,10 @@ data QueryParams =
67
66
-- ^ Canonical representation of the query params, sorted alphabetically
68
67
, qsParams :: [(Text , Text )]
69
68
-- ^ Parameters for RPC calls
70
- , qsRanges :: HM. HashMap Text (Range Integer )
71
- -- ^ Ranges derived from &limit and &offset params
69
+ , qsOffset :: [(EmbedPath , Integer )]
70
+ -- ^ &offset parameter
71
+ , qsLimit :: [(EmbedPath , Integer )]
72
+ -- ^ &limit parameter
72
73
, qsOrder :: [(EmbedPath , [OrderTerm ])]
73
74
-- ^ &order parameters for each level
74
75
, qsLogic :: [(EmbedPath , LogicTree )]
@@ -115,6 +116,8 @@ parse :: Bool -> ByteString -> Either QPError QueryParams
115
116
parse isRpcRead qs = do
116
117
rOrd <- pRequestOrder `traverse` order
117
118
rLogic <- pRequestLogicTree `traverse` logic
119
+ rOffset <- pRequestOffset `traverse` offset
120
+ rLimit <- pRequestLimit `traverse` limit
118
121
rCols <- pRequestColumns columns
119
122
rSel <- pRequestSelect select
120
123
(rFlts, params) <- L. partition hasOp <$> pRequestFilter isRpcRead `traverse` filters
@@ -125,7 +128,7 @@ parse isRpcRead qs = do
125
128
params' = mapMaybe (\ case {(_, Filter (fld, _) (NoOpExpr v)) -> Just (fld,v); _ -> Nothing }) params
126
129
rFltsRoot' = snd <$> rFltsRoot
127
130
128
- return $ QueryParams canonical params' ranges rOrd rLogic rCols rSel rFlts rFltsRoot' rFltsNotRoot rFltsFields rOnConflict
131
+ return $ QueryParams canonical params' rOffset rLimit rOrd rLogic rCols rSel rFlts rFltsRoot' rFltsNotRoot rFltsFields rOnConflict
129
132
where
130
133
hasRootFilter , hasOp :: (EmbedPath , Filter ) -> Bool
131
134
hasRootFilter ([] , _) = True
@@ -138,9 +141,8 @@ parse isRpcRead qs = do
138
141
onConflict = lookupParam " on_conflict"
139
142
columns = lookupParam " columns"
140
143
order = filter (endingIn [" order" ] . fst ) nonemptyParams
141
- limits = filter (endingIn [" limit" ] . fst ) nonemptyParams
142
- -- Replace .offset ending with .limit to be able to match those params later in a map
143
- offsets = first (replaceLast " limit" ) <$> filter (endingIn [" offset" ] . fst ) nonemptyParams
144
+ offset = filter (endingIn [" offset" ] . fst ) nonemptyParams
145
+ limit = filter (endingIn [" limit" ] . fst ) nonemptyParams
144
146
lookupParam :: Text -> Maybe Text
145
147
lookupParam needle = toS <$> join (L. lookup needle qParams)
146
148
nonemptyParams = mapMaybe (\ (k, v) -> (k,) <$> v) qParams
@@ -155,7 +157,7 @@ parse isRpcRead qs = do
155
157
. map (join (***) BS. unpack . second (fromMaybe mempty ))
156
158
$ qString
157
159
158
- endingIn :: [Text ] -> Text -> Bool
160
+ endingIn :: [Text ] -> Text -> Bool
159
161
endingIn xx key = lastWord `elem` xx
160
162
where lastWord = L. last $ T. split (== ' .' ) key
161
163
@@ -164,21 +166,6 @@ parse isRpcRead qs = do
164
166
reserved = [" select" , " columns" , " on_conflict" ]
165
167
reservedEmbeddable = [" order" , " limit" , " offset" , " and" , " or" ]
166
168
167
- replaceLast x s = T. intercalate " ." $ L. init (T. split (== ' .' ) s) <> [x]
168
-
169
- ranges :: HM. HashMap Text (Range Integer )
170
- ranges = HM. unionWith f limitParams offsetParams
171
- where
172
- f rl ro = Range (BoundaryBelow o) (BoundaryAbove $ o + l - 1 )
173
- where
174
- l = fromMaybe 0 $ rangeLimit rl
175
- o = rangeOffset ro
176
-
177
- limitParams =
178
- HM. fromList [(k, restrictRange (readMaybe v) allRange) | (k,v) <- limits]
179
-
180
- offsetParams =
181
- HM. fromList [(k, maybe allRange rangeGeq (readMaybe v)) | (k,v) <- offsets]
182
169
183
170
simpleOperator :: Parser SimpleOperator
184
171
simpleOperator =
@@ -243,11 +230,19 @@ pRequestOrder (k, v) = mapError $ (,) <$> path <*> ord'
243
230
path = fst <$> treePath
244
231
ord' = P. parse pOrder (" failed to parse order (" ++ toS v ++ " )" ) $ toS v
245
232
246
- pRequestRange :: (Text , NonnegRange ) -> Either QPError (EmbedPath , NonnegRange )
247
- pRequestRange (k, v) = mapError $ (,) <$> path <*> pure v
233
+ pRequestOffset :: (Text , Text ) -> Either QPError (EmbedPath , Integer )
234
+ pRequestOffset (k,v) = mapError $ (,) <$> path <*> int
248
235
where
249
236
treePath = P. parse pTreePath (" failed to parse tree path (" ++ toS k ++ " )" ) $ toS k
250
237
path = fst <$> treePath
238
+ int = P. parse pInt (" failed to parse offset parameter (" <> toS v <> " )" ) $ toS v
239
+
240
+ pRequestLimit :: (Text , Text ) -> Either QPError (EmbedPath , Integer )
241
+ pRequestLimit (k,v) = mapError $ (,) <$> path <*> int
242
+ where
243
+ treePath = P. parse pTreePath (" failed to parse tree path (" ++ toS k ++ " )" ) $ toS k
244
+ path = fst <$> treePath
245
+ int = P. parse pInt (" failed to parse limit parameter (" <> toS v <> " )" ) $ toS v
251
246
252
247
pRequestLogicTree :: (Text , Text ) -> Either QPError (EmbedPath , LogicTree )
253
248
pRequestLogicTree (k, v) = mapError $ (,) <$> embedPath <*> logicTree
@@ -842,6 +837,18 @@ pLogicPath = do
842
837
notOp = " not." <> op
843
838
return (filter (/= " not" ) (init path), if " not" `elem` path then notOp else op)
844
839
840
+ pInt :: Parser Integer
841
+ pInt = pPosInt <|> pNegInt
842
+ where
843
+ pPosInt :: Parser Integer
844
+ pPosInt = many1 digit <&> read
845
+
846
+ pNegInt :: Parser Integer
847
+ pNegInt = do
848
+ _ <- char ' -'
849
+ n <- many1 digit
850
+ return ((- 1 ) * read n)
851
+
845
852
pColumns :: Parser [FieldName ]
846
853
pColumns = pFieldName `sepBy1` lexeme (char ' ,' )
847
854
0 commit comments