@@ -14,6 +14,7 @@
1414
1515module Squeel.PostgreSQL.Statement where
1616
17+import Control.Category
1718import Data.Boolean
1819import Data.ByteString (ByteString)
1920import Data.Maybe
@@ -24,15 +25,16 @@ import Data.Vinyl.Functor
2425import GHC.Exts
2526import GHC.OverloadedLabels
2627import GHC.TypeLits
28+import Prelude hiding (id,(.))
2729
2830import qualified Data.ByteString as ByteString
2931
3032import Squeel.PostgreSQL.Schema
3133
3234newtype Expression
33− (params :: [NullityType])
34− (tables :: [(Symbol,[(Symbol,NullityType)])])
35− (ty :: NullityType)
35+ (params :: [ColumnType])
36+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
37+ (ty :: ColumnType)
3638 = UnsafeExpression { renderExpression :: ByteString }
3739
3840class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
@@ -58,7 +60,7 @@ class KnownSymbol column => HasColumn column columns ty
5860 getColumn :: Proxy# column -> Expression params '[table ::: columns] ty
5961 getColumn column = UnsafeExpression $ fromString $ symbolVal' column
6062instance {-# OVERLAPPING #-} KnownSymbol column
61− => HasColumn column ((column ::: ty) ': tys) ty
63+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
6264instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
6365 => HasColumn column (ty' ': table) ty
6466
@@ -96,7 +98,7 @@ unsafeFunction
9698unsafeFunction fn x = UnsafeExpression $ mconcat
9799 [fn, "(", renderExpression x, ")"]
98100
99−instance PGNum ty => Num (Expression params tables (nullity ty)) where
101+instance PGNum ty => Num (Expression params tables ('Required (nullity ty))) where
100102 (+) = unsafeBinaryOp "+"
101103 (-) = unsafeBinaryOp "-"
102104 (*) = unsafeBinaryOp "*"
@@ -104,7 +106,7 @@ instance PGNum ty => Num (Expression params tables (nullity ty)) where
104106 signum = unsafeFunction "sign"
105107 fromInteger = UnsafeExpression . fromString . show
106108
107−instance IsString (Expression params tables (nullity 'PGText)) where
109+instance IsString (Expression params tables ('Required (nullity 'PGText))) where
108110 fromString str = UnsafeExpression $
109111 "E\'" <> fromString (escape =<< str) <> "\'"
110112 where
@@ -119,7 +121,7 @@ instance IsString (Expression params tables (nullity 'PGText)) where
119121 '\\' -> "\\\\"
120122 c -> [c]
121123
122−instance Boolean (Expression params tables ('NotNull 'PGBool)) where
124+instance Boolean (Expression params tables ('Required ('NotNull 'PGBool))) where
123125 true = UnsafeExpression "TRUE"
124126 false = UnsafeExpression "FALSE"
125127 notB = unsafeUnaryOp "NOT"
@@ -127,21 +129,21 @@ instance Boolean (Expression params tables ('NotNull 'PGBool)) where
127129 (||*) = unsafeBinaryOp "OR"
128130
129131type instance BooleanOf (Expression params tables ty) =
130− Expression params tables ('NotNull 'PGBool)
132+ Expression params tables ('Required ('NotNull 'PGBool))
131133
132134coalesce
133− :: [Expression params tables ('Null x)]
134− -> Expression params tables ('NotNull x)
135− -> Expression params tables ('NotNull x)
136−coalesce nulls notNull = UnsafeExpression $ mconcat
135+ :: [Expression params tables ('Required ('Null x))]
136+ -> Expression params tables ('Required ('NotNull x))
137+ -> Expression params tables ('Required ('NotNull x))
138+coalesce nulls isn'tNull = UnsafeExpression $ mconcat
137139 [ "COALESCE("
138140 , ByteString.intercalate ", " $ map renderExpression nulls
139− , ", ", renderExpression notNull
141+ , ", ", renderExpression isn'tNull
140142 , ")"
141143 ]
142144
143145caseWhenThenElse
144− :: [(Expression params tables ('NotNull 'PGBool), Expression params tables ty)]
146+ :: [(Expression params tables ('Required ('NotNull 'PGBool)), Expression params tables ty)]
145147 -> Expression params tables ty
146148 -> Expression params tables ty
147149caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
@@ -171,9 +173,9 @@ instance OrdB (Expression params tables ty) where
171173 (<=*) = unsafeBinaryOp "<="
172174
173175newtype TableExpression
174− (params :: [NullityType])
175− (schema :: [(Symbol,[(Symbol,NullityType)])])
176− (columns :: [(Symbol,NullityType)])
176+ (params :: [ColumnType])
177+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
178+ (columns :: [(Symbol,ColumnType)])
177179 = UnsafeTableExpression { renderTableExpression :: ByteString }
178180
179181data JoinExpression params schema tables where
@@ -189,25 +191,25 @@ data JoinExpression params schema tables where
189191 -> JoinExpression params schema (table ': tables)
190192 Inner
191193 :: Aliased (TableExpression params schema) table
192− -> Expression params (table ': tables) ('NotNull 'PGBool)
194+ -> Expression params (table ': tables) ('Required ('NotNull 'PGBool))
193195 -> JoinExpression params schema tables
194196 -> JoinExpression params schema (table ': tables)
195− LeftOuter
196− :: Aliased (TableExpression params schema) right
197− -> Expression params '[left,right] ('NotNull 'PGBool)
198− -> JoinExpression params schema (tables)
199− -> JoinExpression params schema (NullifyTable right ': left ': tables)
200− RightOuter
201− :: Aliased (TableExpression params schema) right
202− -> Expression params '[left,right] ('NotNull 'PGBool)
203− -> JoinExpression params schema (left : tables)
204− -> JoinExpression params schema (right ': NullifyTable left ': tables)
205− FullOuter
206− :: Aliased (TableExpression params schema) right
207− -> Expression params '[left,right] ('NotNull 'PGBool)
208− -> JoinExpression params schema (left : tables)
209− -> JoinExpression params schema
210− (NullifyTable right ': NullifyTable left ': tables)
197+ -- LeftOuter
198+ -- :: Aliased (TableExpression params schema) right
199+ -- -> Expression params '[left,right] ('Required ('NotNull 'PGBool))
200+ -- -> JoinExpression params schema (tables)
201+ -- -> JoinExpression params schema (NullifyTable right ': left ': tables)
202+ -- RightOuter
203+ -- :: Aliased (TableExpression params schema) right
204+ -- -> Expression params '[left,right] ('Required ('NotNull 'PGBool))
205+ -- -> JoinExpression params schema (left : tables)
206+ -- -> JoinExpression params schema (right ': NullifyTable left ': tables)
207+ -- FullOuter
208+ -- :: Aliased (TableExpression params schema) right
209+ -- -> Expression params '[left,right] ('Required ('NotNull 'PGBool))
210+ -- -> JoinExpression params schema (left : tables)
211+ -- -> JoinExpression params schema
212+ -- (NullifyTable right ': NullifyTable left ': tables)
211213
212214renderJoinExpression :: JoinExpression params schema tables -> ByteString
213215renderJoinExpression = \case
@@ -225,27 +227,27 @@ renderJoinExpression = \case
225227 , " ON "
226228 , renderExpression on
227229 ]
228− LeftOuter table on tables -> mconcat
229− [ renderJoinExpression tables
230− , " LEFT OUTER JOIN "
231− , renderAliased renderTableExpression table
232− , " ON "
233− , renderExpression on
234− ]
235− RightOuter table on tables -> mconcat
236− [ renderJoinExpression tables
237− , " RIGHT OUTER JOIN "
238− , renderAliased renderTableExpression table
239− , " ON "
240− , renderExpression on
241− ]
242− FullOuter table on tables -> mconcat
243− [ renderJoinExpression tables
244− , " FULL OUTER JOIN "
245− , renderAliased renderTableExpression table
246− , " ON "
247− , renderExpression on
248− ]
230+ -- LeftOuter table on tables -> mconcat
231+ -- [ renderJoinExpression tables
232+ -- , " LEFT OUTER JOIN "
233+ -- , renderAliased renderTableExpression table
234+ -- , " ON "
235+ -- , renderExpression on
236+ -- ]
237+ -- RightOuter table on tables -> mconcat
238+ -- [ renderJoinExpression tables
239+ -- , " RIGHT OUTER JOIN "
240+ -- , renderAliased renderTableExpression table
241+ -- , " ON "
242+ -- , renderExpression on
243+ -- ]
244+ -- FullOuter table on tables -> mconcat
245+ -- [ renderJoinExpression tables
246+ -- , " FULL OUTER JOIN "
247+ -- , renderAliased renderTableExpression table
248+ -- , " ON "
249+ -- , renderExpression on
250+ -- ]
249251
250252class KnownSymbol table => HasTable table tables columns
251253 | table tables -> columns where
@@ -266,9 +268,9 @@ instance HasTable table schema columns
266268 fromLabel p = Table $ fromLabel p
267269
268270data Clauses params tables = Clauses
269− { whereClause :: Maybe (Expression params tables ('NotNull 'PGBool))
270− , limitClause :: Maybe (Expression params '[] ('NotNull 'PGInt8))
271− , offsetClause :: Maybe (Expression params '[] ('NotNull 'PGInt8))
271+ { whereClause :: Maybe (Expression params tables ('Required ('NotNull 'PGBool)))
272+ , limitClause :: Maybe (Expression params '[] ('Required ('NotNull 'PGInt8)))
273+ , offsetClause :: Maybe (Expression params '[] ('Required ('NotNull 'PGInt8)))
272274 }
273275
274276instance Monoid (Clauses params tables) where
@@ -292,9 +294,9 @@ instance Monoid (Clauses params tables) where
292294 }
293295
294296data FromExpression
295− (params :: [NullityType])
296− (schema :: [(Symbol,[(Symbol,NullityType)])])
297− (tables :: [(Symbol,[(Symbol,NullityType)])])
297+ (params :: [ColumnType])
298+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
299+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
298300 = FromExpression
299301 { joinExpression :: JoinExpression params schema tables
300302 , clauses :: Clauses params tables
@@ -318,30 +320,30 @@ instance (HasTable table schema columns, table ~ table')
318320 fromLabel p = FromExpression (fromLabel p) mempty
319321
320322where_
321− :: Expression params tables ('NotNull 'PGBool)
323+ :: Expression params tables ('Required ('NotNull 'PGBool))
322324 -> FromExpression params schema tables
323325 -> FromExpression params schema tables
324326where_ wh (FromExpression tabs clauses1) = FromExpression tabs
325327 (clauses1 <> Clauses (Just wh) Nothing Nothing)
326328
327329limit
328− :: Expression params '[] ('NotNull 'PGInt8)
330+ :: Expression params '[] ('Required ('NotNull 'PGInt8))
329331 -> FromExpression params schema tables
330332 -> FromExpression params schema tables
331333limit lim (FromExpression tabs clauses1) = FromExpression tabs
332334 (clauses1 <> Clauses Nothing (Just lim) Nothing)
333335
334336offset
335− :: Expression params '[] ('NotNull 'PGInt8)
337+ :: Expression params '[] ('Required ('NotNull 'PGInt8))
336338 -> FromExpression params schema tables
337339 -> FromExpression params schema tables
338340offset off (FromExpression tabs clauses1) = FromExpression tabs
339341 (clauses1 <> Clauses Nothing Nothing (Just off))
340342
341343newtype Selection
342− (params :: [NullityType])
343− (schema :: [(Symbol,[(Symbol,NullityType)])])
344− (columns :: [(Symbol,NullityType)])
344+ (params :: [ColumnType])
345+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
346+ (columns :: [(Symbol,ColumnType)])
345347 = UnsafeSelection { renderSelection :: ByteString }
346348
347349starFrom
@@ -371,24 +373,22 @@ list `from` tabs = UnsafeSelection $
371373 . rmap (Const . renderAliased renderExpression)
372374
373375newtype Statement
374− (params :: [NullityType])
375− (columns :: [(Symbol,NullityType)])
376− (schema0 :: [(Symbol,[(Symbol,NullityType)])])
377− (schema1 :: [(Symbol,[(Symbol,NullityType)])])
376+ (params :: [ColumnType])
377+ (columns :: [(Symbol,ColumnType)])
378+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
379+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
378380 = UnsafeStatement { renderStatement :: ByteString }
379381
380−(&>>)
381− :: Statement '[] '[] schema0 schema1
382− -> Statement '[] '[] schema1 schema2
383− -> Statement '[] '[] schema0 schema2
384−statement1 &>> statement2 = UnsafeStatement $
385− renderStatement statement1 <> " " <> renderStatement statement2
382+instance Category (Statement '[] '[]) where
383+ id = UnsafeStatement ""
384+ statement2 . statement1 = UnsafeStatement $
385+ renderStatement statement1 <> " " <> renderStatement statement2
386386
387387newtype PreparedStatement
388− (params :: [NullityType])
389− (columns :: [(Symbol,NullityType)])
390− (schema0 :: [(Symbol,[(Symbol,NullityType)])])
391− (schema1 :: [(Symbol,[(Symbol,NullityType)])])
388+ (params :: [ColumnType])
389+ (columns :: [(Symbol,ColumnType)])
390+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
391+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
392392 = UnsafePreparedStatement { renderPreparedStatement :: ByteString }
393393
394394select
@@ -423,19 +423,6 @@ insertInto (Alias table) expressions = UnsafeStatement $ "INSERT INTO "
423423 (\ (expression `As` _) -> Const (renderExpression expression))
424424 expressions
425425
426−createTable
427− :: (KnownSymbol table, KnownColumns columns)
428− => Alias table
429− -> Proxy# columns
430− -> Statement '[] '[] schema ((table ::: columns) ': schema)
431−createTable (Alias table) columns = UnsafeStatement $ mconcat
432− [ "CREATE TABLE "
433− , fromString $ symbolVal' table
434− , " ("
435− , renderColumns columns
436− , ");"
437− ]
438−
439426class KnownSymbol table => DropTable table schema0 schema1
440427 | table schema0 -> schema1 where
441428 dropTable :: Alias table -> Statement '[] '[] schema0 schema1
@@ -458,7 +445,7 @@ update
458445 :: HasTable table schema columns
459446 => Alias table
460447 -> Rec (Aliased (Maybe `Compose` Expression params '[table ::: columns])) columns
461− -> Expression params '[table ::: columns] ('NotNull 'PGBool)
448+ -> Expression params '[table ::: columns] ('Required ('NotNull 'PGBool))
462449 -> Statement params '[] schema schema
463450update (Alias table) columns where' = UnsafeStatement $ mconcat
464451 [ "UPDATE "
@@ -478,3 +465,51 @@ update (Alias table) columns where' = UnsafeStatement $ mconcat
478465 , renderExpression expression
479466 ]
480467 Compose Nothing `As` _ -> Nothing
468+
469+
470+{-----------------------------------------
471+CREATE statements
472+-----------------------------------------}
473+
474+newtype TypeExpression (ty :: ColumnType)
475+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
476+int2 :: TypeExpression ('Required ('Null 'PGInt2))
477+int2 = UnsafeTypeExpression "int2"
478+int4 :: TypeExpression ('Required ('Null 'PGInt4))
479+int4 = UnsafeTypeExpression "int4"
480+int8 :: TypeExpression ('Required ('Null 'PGInt8))
481+int8 = UnsafeTypeExpression "int8"
482+bool :: TypeExpression ('Required ('Null 'PGBool))
483+bool = UnsafeTypeExpression "bool"
484+text :: TypeExpression ('Required ('Null 'PGText))
485+text = UnsafeTypeExpression "text"
486+notNull
487+ :: TypeExpression ('Required ('Null ty))
488+ -> TypeExpression ('Required ('NotNull ty))
489+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <> " NOT NULL"
490+default_
491+ :: Expression '[] '[] ('Required ty)
492+ -> TypeExpression ('Required ty)
493+ -> TypeExpression ('Optional ty)
494+default_ x ty = UnsafeTypeExpression $
495+ renderTypeExpression ty <> " DEFAULT " <> renderExpression x
496+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
497+serial = UnsafeTypeExpression "serial"
498+
499+createTable
500+ :: KnownSymbol table
501+ => Alias table
502+ -> Rec (Aliased TypeExpression) columns
503+ -> Statement '[] '[] schema ((table ::: columns) ': schema)
504+createTable (Alias table) columns = UnsafeStatement $ mconcat
505+ [ "CREATE TABLE "
506+ , fromString $ symbolVal' table
507+ , " ("
508+ , ByteString.intercalate ", " . recordToList $
509+ rmap (Const . renderColumn) columns
510+ , ");"
511+ ]
512+ where
513+ renderColumn :: Aliased TypeExpression x -> ByteString
514+ renderColumn (ty `As` Alias column) =
515+ fromString (symbolVal' column) <> " " <> renderTypeExpression ty