on dev help sign in
Merge pull request #29 from echatav/returning
687bbdeEitan Chatav
squeel-postgresql/squeel-postgresql.cabal−3
@@ -23,7 +23,6 @@ library
23 build-depends:
23 build-depends:
24 aeson
24 aeson
25 , base
25 , base
26− , Boolean
27 , bytestring
26 , bytestring
28 , generics-sop
27 , generics-sop
29 , lifted-base
28 , lifted-base
@@ -46,10 +45,8 @@ test-suite squeel-postgresql-test
46 main-is: Spec.hs
45 main-is: Spec.hs
47 other-modules:
46 other-modules:
48 Squeel.PostgreSQL.StatementSpec
47 Squeel.PostgreSQL.StatementSpec
49− Squeel.PostgreSQL.ValueSpec
50 build-depends:
48 build-depends:
51 base
49 base
52− , Boolean
53 , generics-sop
50 , generics-sop
54 , hspec
51 , hspec
55 , squeel-postgresql
52 , squeel-postgresql
squeel-postgresql/src/Squeel/PostgreSQL/PQ.hs+13−13
@@ -114,11 +114,11 @@ class MonadPQ db m | m -> db where
114 -> m (Maybe (Result ys))
114 -> m (Maybe (Result ys))
115 pqExecNil statement = pqExecParams statement Nil
115 pqExecNil statement = pqExecParams statement Nil
116
116
117− pqExecPrepared
117+ -- pqExecPrepared
118− :: AllZip HasEncoding ps xs
118+ -- :: AllZip HasEncoding ps xs
119− => PreparedStatement ps ys db db
119+ -- => PreparedStatement ps ys db db
120− -> NP I xs
120+ -- -> NP I xs
121− -> m (Maybe (Result ys))
121+ -- -> m (Maybe (Result ys))
122
122
123instance AtkeyPQ PQ where
123instance AtkeyPQ PQ where
124
124
@@ -158,14 +158,14 @@ instance MonadBase IO io => MonadPQ db (PQ db db io) where
158 result <- liftBase $ LibPQ.execParams conn q params' LibPQ.Binary
158 result <- liftBase $ LibPQ.execParams conn q params' LibPQ.Binary
159 return (Result <$> result, Connection conn)
159 return (Result <$> result, Connection conn)
160
160
161− pqExecPrepared (q :: PreparedStatement ps ys db0 db1) params =
161+ -- pqExecPrepared (q :: PreparedStatement ps ys db0 db1) params =
162− PQ $ \ (Connection conn) -> do
162+ -- PQ $ \ (Connection conn) -> do
163− let
163+ -- let
164− paramValues = encodings (Proxy :: Proxy ps) params
164+ -- paramValues = encodings (Proxy :: Proxy ps) params
165− params' = [ Just (param', LibPQ.Binary) | param' <- paramValues ]
165+ -- params' = [ Just (param', LibPQ.Binary) | param' <- paramValues ]
166− result <- liftBase $
166+ -- result <- liftBase $
167− LibPQ.execPrepared conn (renderPreparedStatement q) params' LibPQ.Binary
167+ -- LibPQ.execPrepared conn (renderPreparedStatement q) params' LibPQ.Binary
168− return (Result <$> result, Connection conn)
168+ -- return (Result <$> result, Connection conn)
169
169
170instance Monad m => Applicative (PQ db db m) where
170instance Monad m => Applicative (PQ db db m) where
171 pure x = PQ $ \ conn -> pure (x, conn)
171 pure x = PQ $ \ conn -> pure (x, conn)
squeel-postgresql/src/Squeel/PostgreSQL/Statement.hs+370−202
@@ -1,6 +1,7 @@
1{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
1{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
2{-# LANGUAGE
2{-# LANGUAGE
3 DataKinds
3 DataKinds
4+ , FlexibleContexts
4 , FlexibleInstances
5 , FlexibleInstances
5 , FunctionalDependencies
6 , FunctionalDependencies
6 , GADTs
7 , GADTs
@@ -16,9 +17,8 @@
16
17
17module Squeel.PostgreSQL.Statement where
18module Squeel.PostgreSQL.Statement where
18
19
19−import Data.Boolean
20−import Data.Boolean.Numbers
21import Data.ByteString (ByteString)
20import Data.ByteString (ByteString)
21+import Data.Foldable
22import Data.Maybe
22import Data.Maybe
23import Data.Monoid
23import Data.Monoid
24import Data.Ratio
24import Data.Ratio
@@ -179,6 +179,14 @@ instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty)
179 acosh x = log (x + sqrt (x*x - 1))
179 acosh x = log (x + sqrt (x*x - 1))
180 atanh x = log ((1 + x) / (1 - x)) / 2
180 atanh x = log ((1 + x) / (1 - x)) / 2
181
181
182+atan2
183+ :: PGFloating float
184+ => Expression params tables ('Required (nullity float))
185+ -> Expression params tables ('Required (nullity float))
186+ -> Expression params tables ('Required (nullity float))
187+atan2 y x = UnsafeExpression $
188+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
189+
182class PGCast (ty0 :: PGType) (ty1 :: PGType) where
190class PGCast (ty0 :: PGType) (ty1 :: PGType) where
183 cast
191 cast
184 :: TypeExpression ('Required ('Null ty1))
192 :: TypeExpression ('Required ('Null ty1))
@@ -199,52 +207,52 @@ instance PGCast 'PGInt8 'PGFloat4
199instance PGCast 'PGInt8 'PGFloat8
207instance PGCast 'PGInt8 'PGFloat8
200instance PGCast 'PGInt8 'PGNumeric
208instance PGCast 'PGInt8 'PGNumeric
201
209
210+div
202−instance (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty)
211+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
203− => NumB (Expression params tables ('Required (nullity ty))) where
212+ => Expression params tables ('Required (nullity ty))
204− type IntegerOf (Expression params tables ('Required (nullity ty)))
213+ -> Expression params tables ('Required (nullity ty))
205− = (Expression params tables ('Required (nullity 'PGInt8)))
214+ -> Expression params tables ('Required (nullity ty))
206− fromIntegerB = cast pgtype
215+div = unsafeBinaryOp "/"
207−
216+
217+mod
208−instance (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
218+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
209− => IntegralB (Expression params tables ('Required ('NotNull ty))) where
219+ => Expression params tables ('Required (nullity ty))
210− quot = unsafeBinaryOp "/"
220+ -> Expression params tables ('Required (nullity ty))
211− rem = unsafeBinaryOp "%"
221+ -> Expression params tables ('Required (nullity ty))
212− div = unsafeBinaryOp "/"
213− mod = unsafeBinaryOp "%"
222+mod = unsafeBinaryOp "%"
214− toIntegerB = cast int8
223+
215−
224+truncate
216−instance
225+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
217− ( IntegerOf (Expression params tables ('Required ('NotNull ty)))
226+ => Expression params tables ('Required (nullity frac))
218− ~ (Expression params tables ('Required ('NotNull 'PGInt8)))
227+ -> Expression params tables ('Required (nullity int))
219− , PGNum ty
228+truncate = cast pgtype . truncate'
220− , PGTyped ty
229+ where
221− , PGCast 'PGInt8 ty
230+ truncate'
222− , PGFractional ty
223− )
224− => RealFracB (Expression params tables ('Required ('NotNull ty))) where
231+ :: Expression params tables ('Required (nullity frac))
225− properFraction x = (truncate x, x - unsafeFunction "trunc" x)
232+ -> Expression params tables ('Required (nullity 'PGInt4))
226− truncate = fromIntegerB . unsafeFunction "trunc"
233+ truncate' = unsafeFunction "trunc"
227− round = fromIntegerB . unsafeFunction "round"
234+
228− ceiling = fromIntegerB . unsafeFunction "ceiling"
235+round
229− floor = fromIntegerB . unsafeFunction "floor"
236+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
230−
237+ => Expression params tables ('Required (nullity frac))
231−instance
238+ -> Expression params tables ('Required (nullity int))
239+round = cast pgtype . round'
240+ where
241+ round'
232− ( IntegerOf (Expression params tables ('Required ('NotNull ty)))
242+ :: Expression params tables ('Required (nullity frac))
233− ~ (Expression params tables ('Required ('NotNull 'PGInt8)))
243+ -> Expression params tables ('Required (nullity 'PGInt4))
234− , PGNum ty
244+ round' = unsafeFunction "round"
235− , PGTyped ty
245+
236− , PGCast 'PGInt8 ty
246+ceiling
237− , PGCast 'PGNumeric ty
247+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
238− , PGFloating ty
239− )
240− => RealFloatB (Expression params tables ('Required ('NotNull ty))) where
248+ => Expression params tables ('Required (nullity frac))
241− isNaN x = x ==* UnsafeExpression "\'NaN\'"
249+ -> Expression params tables ('Required (nullity int))
242− isInfinite x = x ==* UnsafeExpression "\'Infinity\'"
250+ceiling = cast pgtype . ceiling'
243− ||* x ==* UnsafeExpression "\'-Infinity\'"
251+ where
244− isNegativeZero x = x ==* UnsafeExpression "-0"
252+ ceiling'
245− isIEEE _ = true
253+ :: Expression params tables ('Required (nullity frac))
246− atan2 y x = UnsafeExpression $
254+ -> Expression params tables ('Required (nullity 'PGInt4))
247− "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
255+ ceiling' = unsafeFunction "ceiling"
248
256
249instance IsString (Expression params tables ('Required (nullity 'PGText))) where
257instance IsString (Expression params tables ('Required (nullity 'PGText))) where
250 fromString str = UnsafeExpression $
258 fromString str = UnsafeExpression $
@@ -261,18 +269,33 @@ instance IsString (Expression params tables ('Required (nullity 'PGText))) where
261 '\\' -> "\\\\"
269 '\\' -> "\\\\"
262 c -> [c]
270 c -> [c]
263
271
264−instance Boolean (Expression params tables ('Required ('NotNull 'PGBool))) where
272+type Predicate params tables = Expression params tables ('Required ('NotNull 'PGBool))
273+
274+true :: Predicate params tables
265− true = UnsafeExpression "TRUE"
275+true = UnsafeExpression "TRUE"
266− false = UnsafeExpression "FALSE"
267− notB = unsafeUnaryOp "NOT"
268− (&&*) = unsafeBinaryOp "AND"
269− (||*) = unsafeBinaryOp "OR"
270
276
271−type instance BooleanOf (Expression params tables ty) =
277+false :: Predicate params tables
272− Expression params tables ('Required ('NotNull 'PGBool))
278+false = UnsafeExpression "FALSE"
279+
280+notB
281+ :: Predicate params tables
282+ -> Predicate params tables
283+notB = unsafeUnaryOp "NOT"
284+
285+(&&*)
286+ :: Predicate params tables
287+ -> Predicate params tables
288+ -> Predicate params tables
289+(&&*) = unsafeBinaryOp "AND"
290+
291+(||*)
292+ :: Predicate params tables
293+ -> Predicate params tables
294+ -> Predicate params tables
295+(||*) = unsafeBinaryOp "OR"
273
296
274caseWhenThenElse
297caseWhenThenElse
275− :: [(Expression params tables ('Required ('NotNull 'PGBool)), Expression params tables ty)]
298+ :: [(Predicate params tables, Expression params tables ty)]
276 -> Expression params tables ty
299 -> Expression params tables ty
277 -> Expression params tables ty
300 -> Expression params tables ty
278caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
301caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
@@ -288,34 +311,64 @@ caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
288 , " END"
311 , " END"
289 ]
312 ]
290
313
314+ifThenElse
315+ :: Predicate params tables
291−instance IfB (Expression params tables ty) where
316+ -> Expression params tables ty
317+ -> Expression params tables ty
318+ -> Expression params tables ty
292− ifB if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
319+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
320+
321+(==*)
322+ :: Expression params tables ty
323+ -> Expression params tables ty
324+ -> Predicate params tables
325+(==*) = unsafeBinaryOp "="
326+
327+(/=*)
328+ :: Expression params tables ty
329+ -> Expression params tables ty
330+ -> Predicate params tables
331+(/=*) = unsafeBinaryOp "<>"
332+
333+(>=*)
334+ :: Expression params tables ty
335+ -> Expression params tables ty
336+ -> Predicate params tables
337+(>=*) = unsafeBinaryOp ">="
338+
339+(<*)
340+ :: Expression params tables ty
341+ -> Expression params tables ty
342+ -> Predicate params tables
343+(<*) = unsafeBinaryOp "<"
293
344
345+(<=*)
294−instance EqB (Expression params tables (optionality ('NotNull ty))) where
346+ :: Expression params tables ty
295− (==*) = unsafeBinaryOp "="
347+ -> Expression params tables ty
348+ -> Predicate params tables
296− (/=*) = unsafeBinaryOp "<>"
349+(<=*) = unsafeBinaryOp "<="
297
350
351+(>*)
298−instance OrdB (Expression params tables (optionality ('NotNull ty))) where
352+ :: Expression params tables ty
353+ -> Expression params tables ty
354+ -> Predicate params tables
299− (>*) = unsafeBinaryOp ">"
355+(>*) = unsafeBinaryOp ">"
300− (>=*) = unsafeBinaryOp ">="
301− (<*) = unsafeBinaryOp "<"
302− (<=*) = unsafeBinaryOp "<="
303
356
304{-----------------------------------------
357{-----------------------------------------
305−table expressions
358+tables
306-----------------------------------------}
359-----------------------------------------}
307
360
308−newtype TableExpression
361+newtype Table
309 (params :: [ColumnType])
362 (params :: [ColumnType])
310 (schema :: [(Symbol,[(Symbol,ColumnType)])])
363 (schema :: [(Symbol,[(Symbol,ColumnType)])])
311 (columns :: [(Symbol,ColumnType)])
364 (columns :: [(Symbol,ColumnType)])
312− = UnsafeTableExpression { renderTableExpression :: ByteString }
365+ = UnsafeTable { renderTable :: ByteString }
313 deriving (Show,Eq)
366 deriving (Show,Eq)
314
367
315class KnownSymbol table => HasTable table tables columns
368class KnownSymbol table => HasTable table tables columns
316 | table tables -> columns where
369 | table tables -> columns where
317− getTable :: Proxy# table -> TableExpression params tables columns
370+ getTable :: Proxy# table -> Table params tables columns
318− getTable table = UnsafeTableExpression $ fromString $ symbolVal' table
371+ getTable table = UnsafeTable $ fromString $ symbolVal' table
319instance {-# OVERLAPPING #-} KnownSymbol table
372instance {-# OVERLAPPING #-} KnownSymbol table
320 => HasTable table ((table ::: columns) ': tables) columns
373 => HasTable table ((table ::: columns) ': tables) columns
321instance {-# OVERLAPPABLE #-}
374instance {-# OVERLAPPABLE #-}
@@ -323,7 +376,7 @@ instance {-# OVERLAPPABLE #-}
323 => HasTable table (table' ': schema) columns
376 => HasTable table (table' ': schema) columns
324
377
325instance HasTable table schema columns
378instance HasTable table schema columns
326− => IsLabel table (TableExpression params schema columns) where
379+ => IsLabel table (Table params schema columns) where
327 fromLabel = getTable
380 fromLabel = getTable
328
381
329{-----------------------------------------
382{-----------------------------------------
@@ -338,13 +391,13 @@ newtype Statement
338 = UnsafeStatement { renderStatement :: ByteString }
391 = UnsafeStatement { renderStatement :: ByteString }
339 deriving (Show,Eq)
392 deriving (Show,Eq)
340
393
341−newtype PreparedStatement
394+-- newtype PreparedStatement
342− (params :: [ColumnType])
395+-- (params :: [ColumnType])
343− (columns :: [(Symbol,ColumnType)])
396+-- (columns :: [(Symbol,ColumnType)])
344− (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
397+-- (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
345− (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
398+-- (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
346− = UnsafePreparedStatement { renderPreparedStatement :: ByteString }
399+-- = UnsafePreparedStatement { renderPreparedStatement :: ByteString }
347− deriving (Show,Eq)
400+-- deriving (Show,Eq)
348
401
349{-----------------------------------------
402{-----------------------------------------
350SELECT statements
403SELECT statements
@@ -352,71 +405,71 @@ SELECT statements
352
405
353data Join params schema tables where
406data Join params schema tables where
354 Table
407 Table
355− :: Aliased (TableExpression params schema) table
408+ :: Aliased (Table params schema) table
356 -> Join params schema '[table]
409 -> Join params schema '[table]
357 Subselect
410 Subselect
358 :: Aliased (Selection params schema) table
411 :: Aliased (Selection params schema) table
359 -> Join params schema '[table]
412 -> Join params schema '[table]
360 Cross
413 Cross
361− :: Aliased (TableExpression params schema) table
414+ :: Aliased (Table params schema) table
362 -> Join params schema tables
415 -> Join params schema tables
363 -> Join params schema (table ': tables)
416 -> Join params schema (table ': tables)
364 Inner
417 Inner
365− :: Aliased (TableExpression params schema) table
418+ :: Aliased (Table params schema) table
366− -> Expression params (table ': tables) ('Required ('NotNull 'PGBool))
419+ -> Predicate params (table ': tables)
367 -> Join params schema tables
420 -> Join params schema tables
368 -> Join params schema (table ': tables)
421 -> Join params schema (table ': tables)
369 LeftOuter
422 LeftOuter
370− :: Aliased (TableExpression params schema) right
423+ :: Aliased (Table params schema) right
371− -> Expression params '[left,right] ('Required ('NotNull 'PGBool))
424+ -> Predicate params '[left,right]
372 -> Join params schema (tables)
425 -> Join params schema (tables)
373 -> Join params schema (NullifyTable right ': left ': tables)
426 -> Join params schema (NullifyTable right ': left ': tables)
374 RightOuter
427 RightOuter
375− :: Aliased (TableExpression params schema) right
428+ :: Aliased (Table params schema) right
376− -> Expression params '[left,right] ('Required ('NotNull 'PGBool))
429+ -> Predicate params '[left,right]
377 -> Join params schema (left : tables)
430 -> Join params schema (left : tables)
378 -> Join params schema (right ': NullifyTable left ': tables)
431 -> Join params schema (right ': NullifyTable left ': tables)
379 FullOuter
432 FullOuter
380− :: Aliased (TableExpression params schema) right
433+ :: Aliased (Table params schema) right
381− -> Expression params '[left,right] ('Required ('NotNull 'PGBool))
434+ -> Predicate params '[left,right]
382 -> Join params schema (left : tables)
435 -> Join params schema (left : tables)
383 -> Join params schema
436 -> Join params schema
384 (NullifyTable right ': NullifyTable left ': tables)
437 (NullifyTable right ': NullifyTable left ': tables)
385
438
386renderJoin :: Join params schema tables -> ByteString
439renderJoin :: Join params schema tables -> ByteString
387renderJoin = \case
440renderJoin = \case
388− Table table -> renderAliased renderTableExpression table
441+ Table table -> renderAliased renderTable table
389 Subselect selection -> "SELECT " <> renderAliased renderSelection selection
442 Subselect selection -> "SELECT " <> renderAliased renderSelection selection
390 Cross table tables -> mconcat
443 Cross table tables -> mconcat
391 [ renderJoin tables
444 [ renderJoin tables
392 , " CROSS JOIN "
445 , " CROSS JOIN "
393− , renderAliased renderTableExpression table
446+ , renderAliased renderTable table
394 ]
447 ]
395 Inner table on tables -> mconcat
448 Inner table on tables -> mconcat
396 [ renderJoin tables
449 [ renderJoin tables
397 , " INNER JOIN "
450 , " INNER JOIN "
398− , renderAliased renderTableExpression table
451+ , renderAliased renderTable table
399 , " ON "
452 , " ON "
400 , renderExpression on
453 , renderExpression on
401 ]
454 ]
402 LeftOuter table on tables -> mconcat
455 LeftOuter table on tables -> mconcat
403 [ renderJoin tables
456 [ renderJoin tables
404 , " LEFT OUTER JOIN "
457 , " LEFT OUTER JOIN "
405− , renderAliased renderTableExpression table
458+ , renderAliased renderTable table
406 , " ON "
459 , " ON "
407 , renderExpression on
460 , renderExpression on
408 ]
461 ]
409 RightOuter table on tables -> mconcat
462 RightOuter table on tables -> mconcat
410 [ renderJoin tables
463 [ renderJoin tables
411 , " RIGHT OUTER JOIN "
464 , " RIGHT OUTER JOIN "
412− , renderAliased renderTableExpression table
465+ , renderAliased renderTable table
413 , " ON "
466 , " ON "
414 , renderExpression on
467 , renderExpression on
415 ]
468 ]
416 FullOuter table on tables -> mconcat
469 FullOuter table on tables -> mconcat
417 [ renderJoin tables
470 [ renderJoin tables
418 , " FULL OUTER JOIN "
471 , " FULL OUTER JOIN "
419− , renderAliased renderTableExpression table
472+ , renderAliased renderTable table
420 , " ON "
473 , " ON "
421 , renderExpression on
474 , renderExpression on
422 ]
475 ]
@@ -425,78 +478,67 @@ instance HasTable table schema columns
425 => IsLabel table (Join params schema '[table ::: columns]) where
478 => IsLabel table (Join params schema '[table ::: columns]) where
426 fromLabel p = Table $ fromLabel p
479 fromLabel p = Table $ fromLabel p
427
480
428−data Clauses params tables = Clauses
481+data TableExpression
429− { whereClause :: Maybe (Expression params tables ('Required ('NotNull 'PGBool)))
430− , limitClause :: Maybe (Expression params '[] ('Required ('NotNull 'PGInt8)))
431− , offsetClause :: Maybe (Expression params '[] ('Required ('NotNull 'PGInt8)))
432− }
433−
434−instance Monoid (Clauses params tables) where
435− mempty = Clauses Nothing Nothing Nothing
436− Clauses wh1 lim1 off1 `mappend` Clauses wh2 lim2 off2 = Clauses
437− { whereClause = case (wh1,wh2) of
438− (Nothing,Nothing) -> Nothing
439− (Just w1,Nothing) -> Just w1
440− (Nothing,Just w2) -> Just w2
441− (Just w1,Just w2) -> Just (w1 &&* w2)
442− , limitClause = case (lim1,lim2) of
443− (Nothing,Nothing) -> Nothing
444− (Just l1,Nothing) -> Just l1
445− (Nothing,Just l2) -> Just l2
446− (Just l1,Just l2) -> Just (l1 `minB` l2)
447− , offsetClause = case (off1,off2) of
448− (Nothing,Nothing) -> Nothing
449− (Just o1,Nothing) -> Just o1
450− (Nothing,Just o2) -> Just o2
451− (Just o1,Just o2) -> Just (o1 + o2)
452− }
453−
454−data From
455 (params :: [ColumnType])
482 (params :: [ColumnType])
456 (schema :: [(Symbol,[(Symbol,ColumnType)])])
483 (schema :: [(Symbol,[(Symbol,ColumnType)])])
457 (tables :: [(Symbol,[(Symbol,ColumnType)])])
484 (tables :: [(Symbol,[(Symbol,ColumnType)])])
458− = From
485+ = TableExpression
459− { fromJoin :: Join params schema tables
486+ { joinClause :: Join params schema tables
460− , fromClauses :: Clauses params tables
487+ , whereClause :: [Predicate params tables]
488+ , limitClause :: [Expression params '[] ('Required ('NotNull 'PGInt8))]
489+ , offsetClause :: [Expression params '[] ('Required ('NotNull 'PGInt8))]
461 }
490 }
462
491
463−join
464− :: Join params schema tables
465− -> From params schema tables
466−join tables = From tables mempty
467−
468−renderFrom :: From params schema tables -> ByteString
492+renderTableExpression :: TableExpression params schema tables -> ByteString
469−renderFrom (From tref (Clauses wh lim off))= mconcat
493+renderTableExpression (TableExpression tables whs' lims' offs') = mconcat
470− [ renderJoin tref
494+ [ renderJoin tables
495+ , renderWheres whs'
496+ , renderLimits lims'
497+ , renderOffsets offs'
498+ ] where
499+ renderWheres = \case
500+ [] -> ""
501+ wh:[] -> " WHERE " <> renderExpression wh
471− , maybe "" ((" WHERE " <>) . renderExpression) wh
502+ wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
503+ renderLimits = \case
504+ [] -> ""
505+ lim:[] -> " LIMIT " <> renderExpression lim
506+ lims -> mconcat
507+ [ " LIMIT LEAST("
472− , maybe "" ((" LIMIT " <>) . renderExpression) lim
508+ , ByteString.intercalate ", " (renderExpression <$> reverse lims)
509+ , ")"
510+ ]
511+ renderOffsets = \case
512+ [] -> ""
513+ off:[] -> " OFFSET " <> renderExpression off
473− , maybe "" ((" OFFSET " <>) . renderExpression) off
514+ off:offs -> " OFFSET " <> renderExpression (foldr (+) off offs)
474− ]
475
515
476instance (HasTable table schema columns, table ~ table')
516instance (HasTable table schema columns, table ~ table')
477− => IsLabel table (From params schema '[table' ::: columns]) where
517+ => IsLabel table (TableExpression params schema '[table' ::: columns]) where
478− fromLabel p = From (fromLabel p) mempty
518+ fromLabel p = join (fromLabel p)
519+
520+join
521+ :: Join params schema tables
522+ -> TableExpression params schema tables
523+join tables = TableExpression tables [] [] []
479
524
480where_
525where_
481− :: Expression params tables ('Required ('NotNull 'PGBool))
526+ :: Predicate params tables
482− -> From params schema tables
527+ -> TableExpression params schema tables
483− -> From params schema tables
528+ -> TableExpression params schema tables
484−where_ wh (From tabs fromClauses1) = From tabs
529+where_ wh (TableExpression tabs whs lims offs) = TableExpression tabs (wh:whs) lims offs
485− (fromClauses1 <> Clauses (Just wh) Nothing Nothing)
486
530
487limit
531limit
488 :: Expression params '[] ('Required ('NotNull 'PGInt8))
532 :: Expression params '[] ('Required ('NotNull 'PGInt8))
489− -> From params schema tables
533+ -> TableExpression params schema tables
490− -> From params schema tables
534+ -> TableExpression params schema tables
491−limit lim (From tabs fromClauses1) = From tabs
535+limit lim (TableExpression tabs whs lims offs) = TableExpression tabs whs (lim:lims) offs
492− (fromClauses1 <> Clauses Nothing (Just lim) Nothing)
493
536
494offset
537offset
495 :: Expression params '[] ('Required ('NotNull 'PGInt8))
538 :: Expression params '[] ('Required ('NotNull 'PGInt8))
496− -> From params schema tables
539+ -> TableExpression params schema tables
497− -> From params schema tables
540+ -> TableExpression params schema tables
498−offset off (From tabs fromClauses1) = From tabs
541+offset off (TableExpression tabs whs lims offs) = TableExpression tabs whs lims (off:offs)
499− (fromClauses1 <> Clauses Nothing Nothing (Just off))
500
542
501newtype Selection
543newtype Selection
502 (params :: [ColumnType])
544 (params :: [ColumnType])
@@ -507,25 +549,25 @@ newtype Selection
507
549
508starFrom
550starFrom
509 :: tables ~ '[table ::: columns]
551 :: tables ~ '[table ::: columns]
510− => From params schema tables
552+ => TableExpression params schema tables
511 -> Selection params schema columns
553 -> Selection params schema columns
512−starFrom tabs = UnsafeSelection $ "* FROM " <> renderFrom tabs
554+starFrom tabs = UnsafeSelection $ "* FROM " <> renderTableExpression tabs
513
555
514dotStarFrom
556dotStarFrom
515 :: HasTable table tables columns
557 :: HasTable table tables columns
516 => Alias table
558 => Alias table
517− -> From params schema tables
559+ -> TableExpression params schema tables
518 -> Selection params schema columns
560 -> Selection params schema columns
519Alias tab `dotStarFrom` tabs = UnsafeSelection $
561Alias tab `dotStarFrom` tabs = UnsafeSelection $
520− fromString (symbolVal' tab) <> ".* FROM " <> renderFrom tabs
562+ fromString (symbolVal' tab) <> ".* FROM " <> renderTableExpression tabs
521
563
522from
564from
523 :: SListI columns
565 :: SListI columns
524 => NP (Aliased (Expression params tables)) columns
566 => NP (Aliased (Expression params tables)) columns
525− -> From params schema tables
567+ -> TableExpression params schema tables
526 -> Selection params schema columns
568 -> Selection params schema columns
527list `from` tabs = UnsafeSelection $
569list `from` tabs = UnsafeSelection $
528− renderList list <> " FROM " <> renderFrom tabs
570+ renderList list <> " FROM " <> renderTableExpression tabs
529 where
571 where
530 renderList
572 renderList
531 = ByteString.intercalate ", "
573 = ByteString.intercalate ", "
@@ -539,34 +581,8 @@ select = UnsafeStatement . ("SELECT " <>) . (<> ";") . renderSelection
539
581
540subselect
582subselect
541 :: Aliased (Selection params schema) table
583 :: Aliased (Selection params schema) table
542− -> From params schema '[table]
584+ -> TableExpression params schema '[table]
543−subselect selection = From
585+subselect selection = join (Subselect selection)
544− { fromJoin = Subselect selection
545− , fromClauses = mempty
546− }
547−
548−{-----------------------------------------
549−INSERT statements
550−-----------------------------------------}
551−
552−insertInto
553− :: (SListI columns, HasTable table schema columns)
554− => Alias table
555− -> NP (Aliased (Expression params '[])) columns
556− -> Statement params '[] schema schema
557−insertInto (Alias table) expressions = UnsafeStatement $ "INSERT INTO "
558− <> fromString (symbolVal' table)
559− <> " (" <> ByteString.intercalate ", " aliases
560− <> ") VALUES ("
561− <> ByteString.intercalate ", " values
562− <> ");"
563− where
564− aliases = hcollapse $ hmap
565− (\ (_ `As` Alias name) -> K (fromString (symbolVal' name)))
566− expressions
567− values = hcollapse $ hmap
568− (\ (expression `As` _) -> K (renderExpression expression))
569− expressions
570
586
571{-----------------------------------------
587{-----------------------------------------
572CREATE statements
588CREATE statements
@@ -732,37 +748,189 @@ instance {-# OVERLAPPABLE #-}
732 DropTable table schema0 schema1
748 DropTable table schema0 schema1
733 => DropTable table (table' ': schema0) (table' ': schema1)
749 => DropTable table (table' ': schema0) (table' ': schema1)
734
750
751+{-----------------------------------------
752+INSERT statements
753+-----------------------------------------}
754+
755+insertInto
756+ :: (SListI columns, HasTable table schema columns)
757+ => Alias table
758+ -> NP (Aliased (Expression params '[])) columns
759+ -> Statement params '[] schema schema
760+insertInto (Alias table) expressions = UnsafeStatement $ "INSERT INTO "
761+ <> fromString (symbolVal' table)
762+ <> " (" <> ByteString.intercalate ", " aliases
763+ <> ") VALUES ("
764+ <> ByteString.intercalate ", " values
765+ <> ");"
766+ where
767+ aliases = hcollapse $ hmap
768+ (\ (_ `As` Alias name) -> K (fromString (symbolVal' name)))
769+ expressions
770+ values = hcollapse $ hmap
771+ (\ (expression `As` _) -> K (renderExpression expression))
772+ expressions
773+
774+insertIntoReturning
775+ :: (SListI columns, SListI results, HasTable table schema columns)
776+ => Alias table
777+ -> NP (Aliased (Expression params '[])) columns
778+ -> NP (Aliased (Expression params '[table ::: columns])) results
779+ -> Statement params results schema schema
780+insertIntoReturning (Alias table) inserts results
781+ = UnsafeStatement . mconcat $
782+ [ "INSERT INTO "
783+ , fromString (symbolVal' table)
784+ , " (" <> ByteString.intercalate ", " aliases
785+ , ") VALUES ("
786+ , ByteString.intercalate ", " values
787+ , ") RETURNING "
788+ , renderList results
789+ , ";"
790+ ]
791+ where
792+ aliases = hcollapse $ hmap
793+ (\ (_ `As` Alias name) -> K (fromString (symbolVal' name)))
794+ inserts
795+ values = hcollapse $ hmap
796+ (\ (expression `As` _) -> K (renderExpression expression))
797+ inserts
798+ renderList
799+ = ByteString.intercalate ", "
800+ . hcollapse
801+ . hmap (K . renderAliased renderExpression)
802+
735{-----------------------------------------
803{-----------------------------------------
736UPDATE statements
804UPDATE statements
737-----------------------------------------}
805-----------------------------------------}
738
806
739−set :: g x -> (Maybe :.: g) x
807+set :: Expression params tables x -> (Maybe :.: Expression params tables) x
740set = Comp . Just
808set = Comp . Just
741
809
742−same :: (Maybe :.: g) x
810+same :: (Maybe :.: Expression params tables) x
743same = Comp Nothing
811same = Comp Nothing
744
812
813+renderSet
814+ :: Aliased (Maybe :.: Expression params '[table ::: columns]) column
815+ -> Maybe ByteString
816+renderSet = \case
817+ Comp (Just expression) `As` Alias column -> Just $ mconcat
818+ [ fromString $ symbolVal' column
819+ , " = "
820+ , renderExpression expression
821+ ]
822+ Comp Nothing `As` _ -> Nothing
823+
745update
824update
746 :: (HasTable table schema columns, SListI columns)
825 :: (HasTable table schema columns, SListI columns)
747 => Alias table
826 => Alias table
748 -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
827 -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
749− -> Expression params '[table ::: columns] ('Required ('NotNull 'PGBool))
828+ -> Predicate params '[table ::: columns]
750 -> Statement params '[] schema schema
829 -> Statement params '[] schema schema
751−update (Alias table) columns where' = UnsafeStatement $ mconcat
830+update (Alias table) columns wh = UnsafeStatement $ mconcat
752 [ "UPDATE "
831 [ "UPDATE "
753 , fromString $ symbolVal' table
832 , fromString $ symbolVal' table
754 , " SET "
833 , " SET "
755 , ByteString.intercalate ", " . catMaybes . hcollapse $
834 , ByteString.intercalate ", " . catMaybes . hcollapse $
756 hmap (K . renderSet) columns
835 hmap (K . renderSet) columns
757− , " WHERE ", renderExpression where'
836+ , " WHERE ", renderExpression wh, ";"
837+ ]
838+
839+updateReturning
840+ :: (SListI columns, SListI results, HasTable table schema columns)
841+ => Alias table
842+ -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
843+ -> Predicate params '[table ::: columns]
844+ -> NP (Aliased (Expression params '[table ::: columns])) results
845+ -> Statement params results schema schema
846+updateReturning (Alias table) updates wh results = UnsafeStatement $ mconcat
847+ [ "UPDATE "
848+ , fromString $ symbolVal' table
849+ , " SET "
850+ , ByteString.intercalate ", " . catMaybes . hcollapse $
851+ hmap (K . renderSet) updates
852+ , " WHERE ", renderExpression wh
853+ , " RETURNING ", renderList results
854+ , ";"
855+ ]
856+ where
857+ renderList
858+ = ByteString.intercalate ", "
859+ . hcollapse
860+ . hmap (K . renderAliased renderExpression)
861+
862+upsertInto
863+ :: (SListI columns, HasTable table schema columns)
864+ => Alias table
865+ -> NP (Aliased (Expression params '[])) columns
866+ -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
867+ -> Predicate params '[table ::: columns]
868+ -> Statement params '[] schema schema
869+upsertInto (Alias table) inserts updates wh = UnsafeStatement . mconcat $
870+ [ "INSERT INTO "
871+ , fromString (symbolVal' table)
872+ , " (" <> ByteString.intercalate ", " aliases
873+ , ") VALUES ("
874+ , ByteString.intercalate ", " values
875+ , ") ON CONFLICT UPDATE "
876+ , fromString $ symbolVal' table
877+ , " SET "
878+ , ByteString.intercalate ", " . catMaybes . hcollapse $
879+ hmap (K . renderSet) updates
880+ , " WHERE ", renderExpression wh, ";"
758 ] where
881 ] where
759− renderSet
882+ aliases = hcollapse $ hmap
760− :: Aliased (Maybe :.: Expression params tables) column
883+ (\ (_ `As` Alias name) -> K (fromString (symbolVal' name)))
761− -> Maybe ByteString
884+ inserts
762− renderSet = \case
885+ values = hcollapse $ hmap
763− Comp (Just expression) `As` Alias column -> Just $ mconcat
886+ (\ (expression `As` _) -> K (renderExpression expression))
887+ inserts
888+
889+upsertIntoReturning
890+ :: (SListI columns, SListI results, HasTable table schema columns)
891+ => Alias table
892+ -> NP (Aliased (Expression params '[])) columns
893+ -> NP (Aliased (Maybe :.: Expression params '[table ::: columns])) columns
894+ -> Predicate params '[table ::: columns]
895+ -> NP (Aliased (Expression params '[table ::: columns])) results
896+ -> Statement params results schema schema
897+upsertIntoReturning (Alias table) inserts updates wh results = UnsafeStatement . mconcat $
898+ [ "INSERT INTO "
899+ , fromString (symbolVal' table)
900+ , " (" <> ByteString.intercalate ", " aliases
901+ , ") VALUES ("
902+ , ByteString.intercalate ", " values
903+ , ") ON CONFLICT UPDATE "
904+ , fromString $ symbolVal' table
905+ , " SET "
906+ , ByteString.intercalate ", " . catMaybes . hcollapse $
907+ hmap (K . renderSet) updates
908+ , " WHERE ", renderExpression wh
909+ , " RETURNING ", renderList results
910+ , ";"
911+ ] where
912+ aliases = hcollapse $ hmap
913+ (\ (_ `As` Alias name) -> K (fromString (symbolVal' name)))
914+ inserts
915+ values = hcollapse $ hmap
916+ (\ (expression `As` _) -> K (renderExpression expression))
917+ inserts
918+ renderList
919+ = ByteString.intercalate ", "
920+ . hcollapse
921+ . hmap (K . renderAliased renderExpression)
922+
923+{-----------------------------------------
924+DELETE statements
925+-----------------------------------------}
926+
927+deleteFrom
928+ :: (HasTable table schema columns, SListI columns)
929+ => Alias table
930+ -> Predicate params '[table ::: columns]
931+ -> Statement params '[] schema schema
932+deleteFrom (Alias table) wh = UnsafeStatement $ mconcat
933+ [ "DELETE FROM "
764− [ fromString $ symbolVal' column
934+ , fromString $ symbolVal' table
765− , " = "
766− , renderExpression expression
935+ , " WHERE ", renderExpression wh, ";"
767− ]
936+ ]
768− Comp Nothing `As` _ -> Nothing
squeel-postgresql/test/Squeel/PostgreSQL/StatementSpec.hs+58−11
@@ -8,7 +8,6 @@
8
8
9module Squeel.PostgreSQL.StatementSpec where
9module Squeel.PostgreSQL.StatementSpec where
10
10
11−import Data.Boolean
12import Data.Function
11import Data.Function
13import Generics.SOP hiding (from)
12import Generics.SOP hiding (from)
14import Test.Hspec
13import Test.Hspec
@@ -25,9 +24,9 @@ spec = do
25 statement :: Statement '[] SumAndCol1 Tables Tables
24 statement :: Statement '[] SumAndCol1 Tables Tables
26 statement = select $
25 statement = select $
27 ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
26 ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
28− `from` (#table1 & where_ true)
27+ `from` (#table1 & where_ (#col1 >* #col2))
29 statement `shouldRenderAs`
28 statement `shouldRenderAs`
30− "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 WHERE TRUE;"
29+ "SELECT (col1 + col2) AS sum, col1 AS col1 FROM table1 AS table1 WHERE (col1 > col2);"
31 it "combines WHEREs using AND" $ do
30 it "combines WHEREs using AND" $ do
32 let
31 let
33 statement :: Statement '[] SumAndCol1 Tables Tables
32 statement :: Statement '[] SumAndCol1 Tables Tables
@@ -53,7 +52,7 @@ spec = do
53 statement :: Statement '[] Columns Tables Tables
52 statement :: Statement '[] Columns Tables Tables
54 statement = select $ starFrom (#table1 & limit 1 & limit 2)
53 statement = select $ starFrom (#table1 & limit 1 & limit 2)
55 statement `shouldRenderAs`
54 statement `shouldRenderAs`
56− "SELECT * FROM table1 AS table1 LIMIT CASE WHEN (1 <= 2) THEN 1 ELSE 2 END;"
55+ "SELECT * FROM table1 AS table1 LIMIT LEAST(1, 2);"
57 it "should render parameters using $ signs" $ do
56 it "should render parameters using $ signs" $ do
58 let
57 let
59 statement :: Statement '[ 'Required ('NotNull 'PGInt8)] Columns Tables Tables
58 statement :: Statement '[ 'Required ('NotNull 'PGInt8)] Columns Tables Tables
@@ -69,12 +68,60 @@ spec = do
69 statement :: Statement '[] Columns Tables Tables
68 statement :: Statement '[] Columns Tables Tables
70 statement = select $ starFrom (#table1 & offset 1 & offset 2)
69 statement = select $ starFrom (#table1 & offset 1 & offset 2)
71 statement `shouldRenderAs` "SELECT * FROM table1 AS table1 OFFSET (1 + 2);"
70 statement `shouldRenderAs` "SELECT * FROM table1 AS table1 OFFSET (1 + 2);"
72− it "correctly render simple INSERTs" $ do
71+ it "correctly renders simple INSERTs" $ do
73 let
72 let
74 statement :: Statement '[] '[] Tables Tables
73 statement :: Statement '[] '[] Tables Tables
75 statement = insertInto #table1 $ 2 `As` #col1 :* 4 `As` #col2 :* Nil
74 statement = insertInto #table1 $ 2 `As` #col1 :* 4 `As` #col2 :* Nil
76 statement `shouldRenderAs`
75 statement `shouldRenderAs`
77 "INSERT INTO table1 (col1, col2) VALUES (2, 4);"
76 "INSERT INTO table1 (col1, col2) VALUES (2, 4);"
77+ it "correctly renders returning INSERTs" $ do
78+ let
79+ statement :: Statement '[] SumAndCol1 Tables Tables
80+ statement = insertIntoReturning #table1
81+ (2 `As` #col1 :* 4 `As` #col2 :* Nil)
82+ ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
83+ statement `shouldRenderAs`
84+ "INSERT INTO table1 (col1, col2) VALUES (2, 4) RETURNING (col1 + col2) AS sum, col1 AS col1;"
85+ it "correctly renders simple UPDATEs" $ do
86+ let
87+ statement :: Statement '[] '[] Tables Tables
88+ statement = update #table1 (set 2 `As` #col1 :* same `As` #col2 :* Nil) (#col1 /=* #col2)
89+ statement `shouldRenderAs`
90+ "UPDATE table1 SET col1 = 2 WHERE (col1 <> col2);"
91+ it "correctly renders returning UPDATEs" $ do
92+ let
93+ statement :: Statement '[] SumAndCol1 Tables Tables
94+ statement = updateReturning #table1
95+ (set 2 `As` #col1 :* same `As` #col2 :* Nil)
96+ (#col1 /=* #col2)
97+ ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
98+ statement `shouldRenderAs`
99+ "UPDATE table1 SET col1 = 2 WHERE (col1 <> col2) RETURNING (col1 + col2) AS sum, col1 AS col1;"
100+ it "correctly renders upsert INSERTs" $ do
101+ let
102+ statement :: Statement '[] '[] Tables Tables
103+ statement = upsertInto #table1
104+ (2 `As` #col1 :* 4 `As` #col2 :* Nil)
105+ (set 2 `As` #col1 :* same `As` #col2 :* Nil)
106+ (#col1 /=* #col2)
107+ statement `shouldRenderAs`
108+ "INSERT INTO table1 (col1, col2) VALUES (2, 4) ON CONFLICT UPDATE table1 SET col1 = 2 WHERE (col1 <> col2);"
109+ it "correctly renders returning upsert INSERTs" $ do
110+ let
111+ statement :: Statement '[] SumAndCol1 Tables Tables
112+ statement = upsertIntoReturning #table1
113+ (2 `As` #col1 :* 4 `As` #col2 :* Nil)
114+ (set 2 `As` #col1 :* same `As` #col2 :* Nil)
115+ (#col1 /=* #col2)
116+ ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
117+ statement `shouldRenderAs`
118+ "INSERT INTO table1 (col1, col2) VALUES (2, 4) ON CONFLICT UPDATE table1 SET col1 = 2 WHERE (col1 <> col2) RETURNING (col1 + col2) AS sum, col1 AS col1;"
119+ it "correctly renders DELETEs" $ do
120+ let
121+ statement :: Statement '[] '[] Tables Tables
122+ statement = deleteFrom #table1 (#col1 ==* #col2)
123+ statement `shouldRenderAs`
124+ "DELETE FROM table1 WHERE (col1 = col2);"
78 it "should be safe against SQL injection in literal text" $ do
125 it "should be safe against SQL injection in literal text" $ do
79 let
126 let
80 statement :: Statement '[] '[] StudentsTable StudentsTable
127 statement :: Statement '[] '[] StudentsTable StudentsTable
@@ -85,9 +132,9 @@ spec = do
85 describe "JOINs" $ do
132 describe "JOINs" $ do
86 let
133 let
87 vals =
134 vals =
88− #orders &. #orderVal `As` #orderVal
135+ (#orders &. #orderVal) `As` #orderVal
89− :* #customers &. #customerVal `As` #customerVal
136+ :* (#customers &. #customerVal) `As` #customerVal
90− :* #shippers &. #shipperVal `As` #shipperVal :* Nil
137+ :* (#shippers &. #shipperVal) `As` #shipperVal :* Nil
91 it "should render CROSS JOINs" $ do
138 it "should render CROSS JOINs" $ do
92 let
139 let
93 statement :: Statement '[] ValueColumns JoinTables JoinTables
140 statement :: Statement '[] ValueColumns JoinTables JoinTables
@@ -103,13 +150,13 @@ spec = do
103 \ CROSS JOIN shippers AS shippers;"
150 \ CROSS JOIN shippers AS shippers;"
104 it "should render INNER JOINs" $ do
151 it "should render INNER JOINs" $ do
105 let
152 let
106− innerJoins :: From '[] JoinTables JoinTables
153+ innerJoins :: TableExpression '[] JoinTables JoinTables
107 innerJoins = join $
154 innerJoins = join $
108 #orders
155 #orders
109 & Inner #customers
156 & Inner #customers
110− (#orders &. #customerID ==* #customers &. #customerID)
157+ ((#orders &. #customerID) ==* (#customers &. #customerID))
111 & Inner #shippers
158 & Inner #shippers
112− (#orders &. #shipperID ==* #shippers &. #shipperID)
159+ ((#orders &. #shipperID) ==* (#shippers &. #shipperID))
113 selection :: Statement '[] ValueColumns JoinTables JoinTables
160 selection :: Statement '[] ValueColumns JoinTables JoinTables
114 selection = select $ vals `from` innerJoins
161 selection = select $ vals `from` innerJoins
115 selection `shouldRenderAs`
162 selection `shouldRenderAs`
squeel-postgresql/test/Squeel/PostgreSQL/ValueSpec.hsdeleted−6
@@ -1,6 +0,0 @@
1−module Squeel.PostgreSQL.ValueSpec where
2−
3−import Test.Hspec
4−
5−spec :: Spec
6−spec = return ()