packages feed

project-m36 1.1.0 → 1.1.1

raw patch · 27 files changed

+294/−116 lines, 27 filesdep +data-defaultdep ~curryer-rpcPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: data-default

Dependency ranges changed: curryer-rpc

API changes (from Hackage documentation)

- ProjectM36.Server.Config: [bindHost] :: ServerConfig -> Hostname
- ProjectM36.Server.Config: [bindPort] :: ServerConfig -> Port
+ ProjectM36.Base: NotificationChangeExpression :: NotificationExpression
+ ProjectM36.Base: NotificationReportNewExpression :: NotificationExpression
+ ProjectM36.Base: NotificationReportOldExpression :: NotificationExpression
+ ProjectM36.Base: data NotificationExpression
+ ProjectM36.Base: instance Control.DeepSeq.NFData ProjectM36.Base.NotificationExpression
+ ProjectM36.Base: instance GHC.Classes.Eq ProjectM36.Base.NotificationExpression
+ ProjectM36.Base: instance GHC.Generics.Generic ProjectM36.Base.NotificationExpression
+ ProjectM36.Base: instance GHC.Show.Show ProjectM36.Base.NotificationExpression
+ ProjectM36.Client: NotificationValidationError :: NotificationName -> NotificationExpression -> RelationalError -> RelationalError
+ ProjectM36.Client: RemoteServerHostAddress :: Hostname -> Port -> RemoteServerAddress
+ ProjectM36.Client: RemoteServerUnixDomainSocketAddress :: FilePath -> RemoteServerAddress
+ ProjectM36.Client: data RemoteServerAddress
+ ProjectM36.Client: defaultRemoteServerAddress :: RemoteServerAddress
+ ProjectM36.Client: defaultServerHostname :: Hostname
+ ProjectM36.Client: instance GHC.Show.Show ProjectM36.Client.RemoteServerAddress
+ ProjectM36.Client: notificationsAsRelation :: SessionId -> Connection -> IO (Either RelationalError Relation)
+ ProjectM36.Client: resolveRemoteServerAddress :: RemoteServerAddress -> IO (SockSpec, SockAddr)
+ ProjectM36.Client.Simple: NotificationValidationError :: NotificationName -> NotificationExpression -> RelationalError -> RelationalError
+ ProjectM36.Error: NotificationValidationError :: NotificationName -> NotificationExpression -> RelationalError -> RelationalError
+ ProjectM36.IsomorphicSchema: notificationsAsData :: Notifications -> [(Text, RelationalExpr, RelationalExpr, RelationalExpr)]
+ ProjectM36.IsomorphicSchema: notificationsAsRelationInSchema :: Notifications -> Schema -> Either RelationalError Relation
+ ProjectM36.RelationalExpression: validateNotification :: Notification -> DatabaseContext -> TransactionGraph -> Either RelationalError Notification
+ ProjectM36.Serialise.Base: instance Codec.Winery.Class.Serialise ProjectM36.Base.NotificationExpression
+ ProjectM36.Server.Config: [bindAddress] :: ServerConfig -> RemoteServerAddress
+ ProjectM36.Server.EntryPoints: handleRetrieveNotificationsAsRelation :: Maybe Timeout -> SessionId -> Connection -> IO (Either RelationalError Relation)
+ ProjectM36.Server.ParseArgs: parseServerAddress :: Parser RemoteServerAddress
+ ProjectM36.Server.ParseArgs: parseUnixDomainSocketPath :: Parser FilePath
+ ProjectM36.Server.RemoteCallTypes: RetrieveNotificationsAsRelation :: SessionId -> RetrieveNotificationsAsRelation
+ ProjectM36.Server.RemoteCallTypes: data RetrieveNotificationsAsRelation
+ ProjectM36.Server.RemoteCallTypes: instance Codec.Winery.Class.Serialise ProjectM36.Server.RemoteCallTypes.RetrieveNotificationsAsRelation
+ ProjectM36.Server.RemoteCallTypes: instance GHC.Generics.Generic ProjectM36.Server.RemoteCallTypes.RetrieveNotificationsAsRelation
- ProjectM36.Base: DifferenceF :: r_aFEe -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: DifferenceF :: r_aFZb -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: EqualsF :: r_aFEe -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: EqualsF :: r_aFZb -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: ExistingRelationF :: Relation -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: ExistingRelationF :: Relation -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: ExtendF :: ExtendTupleExprBase a_atpY -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: ExtendF :: ExtendTupleExprBase a_atFz -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: GroupF :: AttributeNamesBase a_atpY -> Text -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: GroupF :: AttributeNamesBase a_atFz -> Text -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: JoinF :: r_aFEe -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: JoinF :: r_aFZb -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: MakeRelationFromExprsF :: Maybe [AttributeExprBase a_atpY] -> TupleExprsBase a_atpY -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: MakeRelationFromExprsF :: Maybe [AttributeExprBase a_atFz] -> TupleExprsBase a_atFz -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: MakeStaticRelationF :: Attributes -> RelationTupleSet -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: MakeStaticRelationF :: Attributes -> RelationTupleSet -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: NotEqualsF :: r_aFEe -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: NotEqualsF :: r_aFZb -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: ProjectF :: AttributeNamesBase a_atpY -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: ProjectF :: AttributeNamesBase a_atFz -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: RelationValuedAttributeF :: Text -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: RelationValuedAttributeF :: Text -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: RelationVariableF :: Text -> a_atpY -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: RelationVariableF :: Text -> a_atFz -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: RenameF :: Set (Text, Text) -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: RenameF :: Set (Text, Text) -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: RestrictF :: RestrictionPredicateExprBase a_atpY -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: RestrictF :: RestrictionPredicateExprBase a_atFz -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: UngroupF :: Text -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: UngroupF :: Text -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: UnionF :: r_aFEe -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: UnionF :: r_aFZb -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: WithF :: [(WithNameExprBase a_atpY, r_aFEe)] -> r_aFEe -> RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: WithF :: [(WithNameExprBase a_atFz, r_aFZb)] -> r_aFZb -> RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Base: data RelationalExprBaseF (a_atpY :: Type) r_aFEe
+ ProjectM36.Base: data RelationalExprBaseF (a_atFz :: Type) r_aFZb
- ProjectM36.Client: RemoteConnectionInfo :: DatabaseName -> Hostname -> ServiceName -> NotificationCallback -> ConnectionInfo
+ ProjectM36.Client: RemoteConnectionInfo :: DatabaseName -> RemoteServerAddress -> NotificationCallback -> ConnectionInfo
- ProjectM36.Client.Simple: RemoteConnectionInfo :: DatabaseName -> Hostname -> ServiceName -> NotificationCallback -> ConnectionInfo
+ ProjectM36.Client.Simple: RemoteConnectionInfo :: DatabaseName -> RemoteServerAddress -> NotificationCallback -> ConnectionInfo
- ProjectM36.SQL.Select: BetweenOperatorF :: r_a1JsE -> r_a1JsE -> r_a1JsE -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: BetweenOperatorF :: r_a1Kg4 -> r_a1Kg4 -> r_a1Kg4 -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: BinaryOperatorF :: r_a1JsE -> OperatorName -> r_a1JsE -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: BinaryOperatorF :: r_a1Kg4 -> OperatorName -> r_a1Kg4 -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: BooleanLiteralF :: Bool -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: BooleanLiteralF :: Bool -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: BooleanOperatorExprF :: r_a1JsE -> BoolOp -> r_a1JsE -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: BooleanOperatorExprF :: r_a1Kg4 -> BoolOp -> r_a1Kg4 -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: CaseExprF :: [(r_a1JsE, r_a1JsE)] -> Maybe r_a1JsE -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: CaseExprF :: [(r_a1Kg4, r_a1Kg4)] -> Maybe r_a1Kg4 -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: DoubleLiteralF :: Double -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: DoubleLiteralF :: Double -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: ExistsExprF :: Select -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: ExistsExprF :: Select -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: FunctionApplicationF :: FuncName -> [r_a1JsE] -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: FunctionApplicationF :: FuncName -> [r_a1Kg4] -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: IdentifierF :: n_a1zvF -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: IdentifierF :: n_a1Aj1 -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: InExprF :: InFlag -> r_a1JsE -> InPredicateValue -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: InExprF :: InFlag -> r_a1Kg4 -> InPredicateValue -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: IntegerLiteralF :: Integer -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: IntegerLiteralF :: Integer -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: NullLiteralF :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: NullLiteralF :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: PostfixOperatorF :: r_a1JsE -> OperatorName -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: PostfixOperatorF :: r_a1Kg4 -> OperatorName -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: PrefixOperatorF :: OperatorName -> r_a1JsE -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: PrefixOperatorF :: OperatorName -> r_a1Kg4 -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: QuantifiedComparisonF :: r_a1JsE -> ComparisonOperator -> QuantifiedComparisonPredicate -> Select -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: QuantifiedComparisonF :: r_a1Kg4 -> ComparisonOperator -> QuantifiedComparisonPredicate -> Select -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: StringLiteralF :: Text -> ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: StringLiteralF :: Text -> ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.SQL.Select: [caseElseF] :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE -> Maybe r_a1JsE
+ ProjectM36.SQL.Select: [caseElseF] :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4 -> Maybe r_a1Kg4
- ProjectM36.SQL.Select: [caseWhensF] :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE -> [(r_a1JsE, r_a1JsE)]
+ ProjectM36.SQL.Select: [caseWhensF] :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4 -> [(r_a1Kg4, r_a1Kg4)]
- ProjectM36.SQL.Select: [qcExprF] :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE -> r_a1JsE
+ ProjectM36.SQL.Select: [qcExprF] :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4 -> r_a1Kg4
- ProjectM36.SQL.Select: [qcOperatorF] :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE -> ComparisonOperator
+ ProjectM36.SQL.Select: [qcOperatorF] :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4 -> ComparisonOperator
- ProjectM36.SQL.Select: [qcPredicateF] :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE -> QuantifiedComparisonPredicate
+ ProjectM36.SQL.Select: [qcPredicateF] :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4 -> QuantifiedComparisonPredicate
- ProjectM36.SQL.Select: [qcQueryF] :: ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE -> Select
+ ProjectM36.SQL.Select: [qcQueryF] :: ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4 -> Select
- ProjectM36.SQL.Select: data ScalarExprBaseF (n_a1zvF :: Type) r_a1JsE
+ ProjectM36.SQL.Select: data ScalarExprBaseF (n_a1Aj1 :: Type) r_a1Kg4
- ProjectM36.Server.Config: ServerConfig :: PersistenceStrategy -> Bool -> DatabaseName -> Hostname -> Port -> [String] -> Int -> Bool -> ServerConfig
+ ProjectM36.Server.Config: ServerConfig :: PersistenceStrategy -> Bool -> DatabaseName -> RemoteServerAddress -> [String] -> Int -> Bool -> ServerConfig

Files

Changelog.markdown view
@@ -1,3 +1,9 @@+# 2025-03-30 (v1.1.1)++* upgrade to curryer-rpc 0.4.0 which includes support for IPv6 and Unix domain socket communications++This improvement changes the connectProjectM36 API. When connecting to a database over IPv4 or IPv6, use `RemoteConnectionInfo <dbname> (RemoteServerHostAddress <String ip_addr> <Int port>) <NotificationCallback>`.+ # 2024-08-25 (v1.1.0)  * add support for GHC 9.6, GHC 9.8, and GHC 9.10
examples/Plantfarm.hs view
@@ -2,12 +2,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingVia #-}--- The calls to 'S.raise' from the @scotty@ package are /deprecated/--- TODO:--- Replace 'raise' with 'throw'------ Until then, supproess the deprecation warning-{-# OPTIONS_GHC -fno-warn-deprecations #-} module Main   ( main     -- * Other example exports@@ -17,14 +11,15 @@   , updatePlant   ) where ++import Control.Monad.Catch (Exception) import Codec.Winery (Serialise, WineryVariant(WineryVariant)) import Control.DeepSeq (NFData) import Data.Aeson (FromJSON, ToJSON(toEncoding, toJSON)) import Data.Data (Proxy(Proxy)) import Data.Either (lefts, rights) import Data.Functor (($>))-import Data.Text as T (Text)-import qualified Data.Text.Lazy as TL (pack)+import Data.Text as T (Text,pack) import GHC.Generics (Generic) import qualified ProjectM36.Base as Base import ProjectM36.DatabaseContext@@ -207,7 +202,7 @@      --  deleting all plants at a specific stage     S.delete "/plants" $ do-      s <- S.pathParam "name"+      s <- S.queryParam "name"       e <- liftIO $ deletePlantByName c s       p <- handleWebError e       S.json p@@ -218,15 +213,20 @@       ps <- handleWebErrors e       S.json ps +data WebError = WebError T.Text+ deriving Show++instance Exception WebError+ handleWebError :: Either Err b -> S.ActionM b-handleWebError (Left e) = S.raise . TL.pack $ "An error occurred:\n" <> show e+handleWebError (Left e) = S.throw (WebError (T.pack $ "An error occurred:\n" <> show e)) handleWebError (Right v) = pure v  handleWebErrors :: [Either Err b] -> S.ActionM [b] handleWebErrors e = do   case lefts e of     [] -> pure (rights e)-    l -> S.raise . TL.pack $ "Errors occurred:\n" <> concatMap ((<> "\n") . show) l+    l -> S.throw (WebError (T.pack $ "Errors occurred:\n" <> concatMap ((<>"\n") . show) l))   -- |    watering a plant and thereby possibly updating its stage
examples/SimpleClient.hs view
@@ -9,7 +9,7 @@ main :: IO () main = do   -- 1. create a ConnectionInfo-  let connInfo = RemoteConnectionInfo "mytestdb" "127.0.0.1" (show defaultServerPort) emptyNotificationCallback+  let connInfo = RemoteConnectionInfo "mytestdb" defaultRemoteServerAddress emptyNotificationCallback   -- 2. connected to the remote database   eConn <- connectProjectM36 connInfo   case eConn of
project-m36.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: project-m36-Version: 1.1.0+Version: 1.1.1 License: MIT --note that this license specification is erroneous and only labeled MIT to appease hackage which does not recognize public domain packages in cabal >2.2- Project:M36 is dedicated to the public domain Build-Type: Simple@@ -89,7 +89,7 @@                   cryptohash-sha256,                   text-manipulate >= 0.2.0.1 && < 0.4,                   winery >= 1.4,-                  curryer-rpc>=0.3.7,+                  curryer-rpc>=0.4.0,                   network,                   async,                   vector-instances,@@ -271,7 +271,9 @@                    http-conduit,                    modern-uri,                    http-types,-                   recursion-schemes+                   recursion-schemes,+                   data-default < 0.8+                   --data-default 0.8.0.0 breaks http-tls-client     Other-Modules: TutorialD.Interpreter,                    TutorialD.Interpreter.Base,                    TutorialD.Interpreter.DatabaseContextExpr,@@ -595,7 +597,7 @@ Executable Example-Plantfarm     Default-Language: Haskell2010     Default-Extensions: OverloadedStrings-    Build-Depends:  aeson, barbies, base, containers, deepseq, hashable, project-m36, random, scotty >= 0.22, text, winery+    Build-Depends:  aeson, barbies, base, containers, deepseq, hashable, project-m36, random, scotty >= 0.22, text, winery, exceptions     Main-Is: examples/Plantfarm.hs     GHC-Options: -Wall -threaded 
src/bin/ProjectM36/Cli.hs view
@@ -4,6 +4,7 @@ import qualified ProjectM36.Client as C import qualified Data.Text as T import ProjectM36.Base+import ProjectM36.Client (RemoteServerAddress(..)) import System.Console.Haskeline import Control.Exception import System.IO@@ -28,7 +29,7 @@ type ParserError = ParseErrorBundle T.Text Void  data InterpreterConfig = LocalInterpreterConfig PersistenceStrategy HeadName (Maybe DirectExecute) [GhcPkgPath] CheckFS |-                         RemoteInterpreterConfig C.Hostname C.Port C.DatabaseName HeadName (Maybe TutorialDExec) CheckFS+                         RemoteInterpreterConfig RemoteServerAddress C.DatabaseName HeadName (Maybe TutorialDExec) CheckFS  outputNotificationCallback :: C.NotificationCallback outputNotificationCallback notName evaldNot = hPutStrLn stderr $ "Notification received " ++ show notName ++ ":\n" ++ "\n" ++ prettyEvaluatedNotification evaldNot@@ -63,7 +64,7 @@  parseArgs :: Parser InterpreterConfig parseArgs = LocalInterpreterConfig <$> parsePersistenceStrategy <*> parseHeadName <*> parseDirectExecute <*> many parseGhcPkgPath <*> parseCheckFS <|>-            RemoteInterpreterConfig <$> parseHostname "127.0.0.1" <*> parsePort C.defaultServerPort <*> parseDatabaseName <*> parseHeadName <*> parseDirectExecute <*> parseCheckFS+            RemoteInterpreterConfig <$> parseServerAddress <*> parseDatabaseName <*> parseHeadName <*> parseDirectExecute <*> parseCheckFS  parseHeadName :: Parser HeadName                parseHeadName = option auto (long "head" <>@@ -120,19 +121,19 @@  connectionInfoForConfig :: InterpreterConfig -> DatabaseContext -> C.ConnectionInfo connectionInfoForConfig (LocalInterpreterConfig pStrategy _ _ ghcPkgPaths _) defaultDBContext = C.InProcessConnectionInfo pStrategy outputNotificationCallback ghcPkgPaths defaultDBContext-connectionInfoForConfig (RemoteInterpreterConfig remoteHost remotePort remoteDBName _ _ _) _ = C.RemoteConnectionInfo remoteDBName remoteHost (show remotePort) outputNotificationCallback+connectionInfoForConfig (RemoteInterpreterConfig remoteAddress remoteDBName _ _ _) _ = C.RemoteConnectionInfo remoteDBName remoteAddress outputNotificationCallback  headNameForConfig :: InterpreterConfig -> HeadName headNameForConfig (LocalInterpreterConfig _ headn _ _ _) = headn-headNameForConfig (RemoteInterpreterConfig _ _ _ headn _ _) = headn+headNameForConfig (RemoteInterpreterConfig _ _ headn _ _) = headn  directExecForConfig :: InterpreterConfig -> Maybe String directExecForConfig (LocalInterpreterConfig _ _ t _ _) = t-directExecForConfig (RemoteInterpreterConfig _ _ _ _ t _) = t+directExecForConfig (RemoteInterpreterConfig _ _ _ t _) = t  checkFSForConfig :: InterpreterConfig -> Bool checkFSForConfig (LocalInterpreterConfig _ _ _ _ c) = c-checkFSForConfig (RemoteInterpreterConfig _ _ _ _ _ c) = c+checkFSForConfig (RemoteInterpreterConfig _ _ _ _ c) = c  persistenceStrategyForConfig :: InterpreterConfig -> Maybe PersistenceStrategy persistenceStrategyForConfig (LocalInterpreterConfig strat _ _ _ _) = Just strat
src/bin/ProjectM36/Server/RemoteCallTypes/Json.hs view
@@ -181,6 +181,9 @@ instance ToJSON RelationalError instance FromJSON RelationalError +instance ToJSON NotificationExpression+instance FromJSON NotificationExpression+ instance ToJSON SQLError instance FromJSON SQLError 
src/bin/ProjectM36/Server/WebSocket.hs view
@@ -92,7 +92,7 @@  --this creates a new database for each connection- perhaps not what we want (?) createConnection :: WS.Connection -> DatabaseName -> Port -> Hostname -> IO (Either ConnectionError Connection)-createConnection wsconn dbname port host = connectProjectM36 (RemoteConnectionInfo dbname host (show port) (notificationCallback wsconn))+createConnection wsconn dbname port host = connectProjectM36 (RemoteConnectionInfo dbname (RemoteServerHostAddress host port) (notificationCallback wsconn))  handleResponse :: WS.Connection -> Connection -> Response -> IO () handleResponse conn dbconn resp =
src/bin/ProjectM36/Server/WebSocket/websocket-server.hs view
@@ -16,19 +16,22 @@ import ProjectM36.Server.Config import ProjectM36.Server.ParseArgs import ProjectM36.Server.WebSocket+import ProjectM36.Client (RemoteServerAddress(..))  main :: IO () main = do   -- launch normal project-m36-server   addressMVar <- newEmptyMVar-  wsConfig <- parseWSConfigWithDefaults (defaultServerConfig {bindPort = 8000, bindHost = "127.0.0.1"})+  wsConfig <- parseWSConfigWithDefaults (defaultServerConfig {bindAddress = RemoteServerHostAddress "127.0.0.1" 8000})    --usurp the serverConfig for our websocket server and make the proxied server run locally   let serverConfig = wsServerConfig wsConfig-      wsHost = bindHost serverConfig-      wsPort = bindPort serverConfig+      wsAddress = bindAddress serverConfig+      (wsHost, wsPort) = case wsAddress of+                           RemoteServerHostAddress host port -> (host, port)+                           _ -> error "expected host-based address"       serverHost = "127.0.0.1"-      serverConfig' = serverConfig {bindPort = 0, bindHost = serverHost}+      serverConfig' = serverConfig {bindAddress = RemoteServerHostAddress serverHost 0}       configCertificateFile = tlsCertificatePath wsConfig       configKeyFile = tlsKeyPath wsConfig 
src/bin/TutorialD/Interpreter/RODatabaseContextOperator.hs view
@@ -31,6 +31,7 @@   ShowRelationVariables :: RODatabaseContextOperator   ShowAtomFunctions :: RODatabaseContextOperator   ShowDatabaseContextFunctions :: RODatabaseContextOperator+  ShowNotifications :: RODatabaseContextOperator   ShowDataFrame :: DF.DataFrameExpr -> RODatabaseContextOperator   GetDDLHash :: RODatabaseContextOperator   ShowDDL :: RODatabaseContextOperator@@ -65,6 +66,9 @@ showDatabaseContextFunctionsP :: Parser RODatabaseContextOperator showDatabaseContextFunctionsP = colonOp ":showdatabasecontextfunctions" >> pure ShowDatabaseContextFunctions +showNotificationsP :: Parser RODatabaseContextOperator+showNotificationsP = colonOp ":shownotifications" >> pure ShowNotifications+ quitP :: Parser RODatabaseContextOperator quitP = do   colonOp ":quit"@@ -90,6 +94,7 @@              <|> showTypesP              <|> showAtomFunctionsP              <|> showDatabaseContextFunctionsP+             <|> showNotificationsP              <|> showDataFrameP              <|> ddlHashP              <|> showDDLP@@ -158,6 +163,12 @@      evalRODatabaseContextOp sessionId conn ShowDatabaseContextFunctions = do   eRel <- C.databaseContextFunctionsAsRelation sessionId conn+  case eRel of+    Left err -> pure $ DisplayErrorResult (T.pack (show err))+    Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel))+    +evalRODatabaseContextOp sessionId conn ShowNotifications = do+  eRel <- C.notificationsAsRelation sessionId conn   case eRel of     Left err -> pure $ DisplayErrorResult (T.pack (show err))     Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel))
src/bin/TutorialD/Interpreter/RelationalExpr.hs view
@@ -158,7 +158,7 @@ relTerm :: RelationalMarkerExpr a => Parser (RelationalExprBase a) relTerm = parens relExprP           <|> makeRelationP-          <|> (relVarP <* notFollowedBy "(")+          <|> relVarP           <|> relationValuedAttributeP  relationValuedAttributeP :: RelationalMarkerExpr a => Parser (RelationalExprBase a)
src/bin/TutorialD/Interpreter/Types.hs view
@@ -78,6 +78,6 @@ monoTypeConstructorP = ADTypeConstructor <$> typeConstructorNameP <*> pure [] <|>                        TypeVariable <$> typeVariableIdentifierP                    -+-- regular, uncapitalized name, but don't conflate it with a function (followed by parenthesis) relVarNameP :: Parser RelVarName-relVarNameP = uncapitalizedOrQuotedIdentifier <* spaceConsumer+relVarNameP = uncapitalizedOrQuotedIdentifier <* notFollowedBy "(" <* spaceConsumer
src/lib/ProjectM36/AtomType.hs view
@@ -81,7 +81,7 @@   --if any two maps have the same key and different values, this indicates a type arg mismatch     let typeVarMapFolder valMap acc = case acc of           Left err -> Left err-          Right accMap -> +          Right accMap -> do             case resolveAtomTypesInTypeVarMap valMap accMap of               Left (TypeConstructorTypeVarMissing _) -> Left (DataConstructorTypeVarsMismatch (DCD.name dCons) accMap valMap)               Left err -> Left err@@ -232,7 +232,7 @@      --given two atom types, try to resolve type variables                                      resolveAtomType :: AtomType -> AtomType -> Either RelationalError AtomType  -resolveAtomType (ConstructedAtomType tConsName resolvedTypeVarMap) (ConstructedAtomType _ unresolvedTypeVarMap) =+resolveAtomType (ConstructedAtomType tConsName resolvedTypeVarMap) (ConstructedAtomType _ unresolvedTypeVarMap) = do   ConstructedAtomType tConsName <$> resolveAtomTypesInTypeVarMap resolvedTypeVarMap unresolvedTypeVarMap  resolveAtomType typeFromRelation unresolvedType = if typeFromRelation == unresolvedType then                                                     Right typeFromRelation@@ -267,10 +267,13 @@               resSubType <- resolveAtomType resType subType               pure (resKey, resSubType)             TypeVariableType _ -> pure (resKey, resType)-            typ -> if typ == resType then-                     pure (resKey, resType)+            -- in the data type F (Maybe Integer) (Maybe Integer), if the first Maybe Integer is "Nothing", then we want to prefer the concrete "unresolved type"- improvement: create different Haskell types for unresolved and concrete types using Void phantom type for TypeVariableType for concrete type+            typ -> if isResolvedType typ then+                       pure (resKey, typ)+                   else if isResolvedType resType && typ == resType then+                       pure (resKey, resType)                    else-                     Left $ AtomTypeMismatchError typ resType+                       Left $ AtomTypeMismatchError typ resType           Nothing ->             pure (resKey, resType) --swipe the missing type var from the expected map   tVarList <- mapM (uncurry resolveTypePair) (M.toList resolvedTypeMap)
src/lib/ProjectM36/Base.hs view
@@ -289,6 +289,11 @@   }   deriving (Show, Eq, Generic, NFData) +data NotificationExpression = NotificationChangeExpression |+                              NotificationReportOldExpression |+                              NotificationReportNewExpression+                              deriving (Show, Eq, Generic, NFData)+ type TypeVarName = StringType    -- | Metadata definition for type constructors such as @data Either a b@.
src/lib/ProjectM36/Client.hs view
@@ -36,6 +36,7 @@        transactionGraphAsRelation,        relationVariablesAsRelation,        registeredQueriesAsRelation,+       notificationsAsRelation,        ddlAsRelation,        ProjectM36.Client.atomFunctionsAsRelation,        disconnectedTransactionIsDirty,@@ -108,7 +109,11 @@        AtomExprBase(..),        RestrictionPredicateExprBase(..),        withTransaction,-       basicDatabaseContext+       basicDatabaseContext,+       RemoteServerAddress(..),+       resolveRemoteServerAddress,+       defaultRemoteServerAddress,+       defaultServerHostname        ) where import ProjectM36.Base hiding (inclusionDependencies) --defined in this module as well import qualified ProjectM36.Base as B@@ -172,16 +177,20 @@ import Data.Time.Clock import qualified Network.RPC.Curryer.Client as RPC import qualified Network.RPC.Curryer.Server as RPC-import Network.Socket (Socket, AddrInfo(..), getAddrInfo, defaultHints, AddrInfoFlag(..), SocketType(..), ServiceName, hostAddressToTuple, SockAddr(..))+import Network.Socket (Socket, AddrInfo(..), getAddrInfo, defaultHints, SocketType(..), ServiceName, SockAddr, Family(..), SockAddr(..)) import GHC.Conc (unsafeIOToSTM) import ProjectM36.SQL.Select as SQL import ProjectM36.SQL.DBUpdate as SQL-import ProjectM36.SQL.Convert +import ProjectM36.SQL.Convert+import Streamly.Internal.Network.Socket (SockSpec(..))  type Hostname = String- type Port = Word16 +data RemoteServerAddress = RemoteServerHostAddress Hostname Port |+                           RemoteServerUnixDomainSocketAddress FilePath+                           deriving (Show)+ -- | The type for notifications callbacks in the client. When a registered notification fires due to a changed relational expression evaluation, the server propagates the notifications to the clients in the form of the callback. type NotificationCallback = NotificationName -> EvaluatedNotification -> IO () @@ -203,7 +212,7 @@  -- | Construct a 'ConnectionInfo' to describe how to make the 'Connection'. The database can be run within the current process or running remotely via RPC. data ConnectionInfo = InProcessConnectionInfo PersistenceStrategy NotificationCallback [GhcPkgPath] DatabaseContext |-                      RemoteConnectionInfo DatabaseName Hostname ServiceName NotificationCallback+                      RemoteConnectionInfo DatabaseName RemoteServerAddress NotificationCallback                        type EvaluatedNotifications = M.Map NotificationName EvaluatedNotification @@ -234,10 +243,14 @@ defaultHeadName :: HeadName defaultHeadName = "master" +-- | Use this for connecting to the default remote server.+defaultRemoteServerAddress :: RemoteServerAddress+defaultRemoteServerAddress = RemoteServerHostAddress "127.0.0.1" defaultServerPort+ -- | Create a connection configuration which connects to the localhost on the default server port and default server database name. The configured notification callback is set to ignore all events. defaultRemoteConnectionInfo :: ConnectionInfo defaultRemoteConnectionInfo =-  RemoteConnectionInfo defaultDatabaseName defaultServerHostname (show defaultServerPort) emptyNotificationCallback+  RemoteConnectionInfo defaultDatabaseName defaultRemoteServerAddress emptyNotificationCallback  defaultServerHostname :: Hostname defaultServerHostname = "localhost"@@ -265,6 +278,26 @@     Left err -> hPutStrLn stderr ("Warning: Haskell scripting disabled: " ++ show err) >> pure Nothing --not a fatal error, but the scripting feature must be disabled     Right s -> pure (Just s) +-- | Resolve a server address using DNS, if necessary. The caller is expected to set any necessary socket options afterwards.+resolveRemoteServerAddress :: RemoteServerAddress -> IO (SockSpec, SockAddr)+resolveRemoteServerAddress (RemoteServerHostAddress hostname port) = do+  let addrHints = defaultHints { addrSocketType = Stream }+  hostAddrs <- getAddrInfo (Just addrHints) (Just hostname) (Just (show port))+  case hostAddrs of+    [] -> error "getAddrInfo returned zero matches"+    (AddrInfo _flags family socketType proto sockAddr _canonicalName:_) -> do+      let sockSpec = SockSpec { sockFamily = family,+                                sockType = socketType,+                                sockProto = proto,+                                sockOpts = [] }+      pure (sockSpec, sockAddr)+resolveRemoteServerAddress (RemoteServerUnixDomainSocketAddress sockPath) = do+  let sockSpec = SockSpec { sockFamily = AF_UNIX,+                            sockType = Stream,+                            sockProto = 0,+                            sockOpts = [] }+      sockAddr = SockAddrUnix sockPath+  pure (sockSpec, sockAddr)  -- | To create a 'Connection' to a remote or local database, create a 'ConnectionInfo' and call 'connectProjectM36'. connectProjectM36 :: ConnectionInfo -> IO (Either ConnectionError Connection)@@ -294,36 +327,25 @@     MinimalPersistence dbdir -> connectPersistentProjectM36 strat NoDiskSync dbdir freshGraph notificationCallback ghcPkgPaths     CrashSafePersistence dbdir -> connectPersistentProjectM36 strat FsyncDiskSync dbdir freshGraph notificationCallback ghcPkgPaths         -connectProjectM36 (RemoteConnectionInfo dbName hostName servicePort notificationCallback) = do-  --TODO- add notification callback thread-  let resolutionHints = defaultHints { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV],-                                       addrSocketType = Stream-                                       }-  resolved <- getAddrInfo (Just resolutionHints) (Just hostName) (Just servicePort)-  case resolved of-    [] -> error ("DNS resolution failed for" <> hostName <> ":" <> servicePort)-    addrInfo:_ -> do-      --supports IPv4 only for now-      let (port, addr) = case addrAddress addrInfo of-                           SockAddrInet p a -> (p, a)-                           _ -> error "no IPv4 address available (IPv6 not implemented)"-          notificationHandlers =-            [RPC.ClientAsyncRequestHandler $-             \(NotificationMessage notifications') ->-               forM_ (M.toList notifications') (uncurry notificationCallback)-            ]-      let connectExcHandler (e :: IOException) = pure $ Left (IOExceptionError e)-      eConn <- (Right <$> RPC.connect notificationHandlers (hostAddressToTuple addr) port) `catch` connectExcHandler-      case eConn of-        Left err -> pure (Left err)-        Right conn -> do-          eRet <- RPC.call conn (Login dbName)-          case eRet of-            Left err -> error (show err)-            Right False -> error "wtf"-            Right True ->+connectProjectM36 (RemoteConnectionInfo dbName remoteAddress notificationCallback) = do+  (sockSpec, sockAddr) <- resolveRemoteServerAddress remoteAddress+  let notificationHandlers =+        [RPC.ClientAsyncRequestHandler $+          \(NotificationMessage notifications') ->+            forM_ (M.toList notifications') (uncurry notificationCallback)+        ]+      connectExcHandler (e :: IOException) = pure $ Left (IOExceptionError e)+  eConn <- (Right <$> RPC.connect notificationHandlers sockSpec sockAddr) `catch` connectExcHandler+  case eConn of+    Left err -> pure (Left err)+    Right conn -> do+      eRet <- RPC.call conn (Login dbName)+      case eRet of+        Left err -> error (show err)+        Right False -> error "wtf"+        Right True ->       --TODO handle connection errors!-              pure (Right (RemoteConnection (RemoteConnectionConf conn)))+          pure (Right (RemoteConnection (RemoteConnectionConf conn)))  --convert RPC errors into exceptions convertRPCErrors :: RPC.ConnectionError -> IO a@@ -876,7 +898,18 @@       Right (session, _) ->         pure (DCF.databaseContextFunctionsAsRelation (dbcFunctions (concreteDatabaseContext session))) -databaseContextFunctionsAsRelation sessionId conn@(RemoteConnection _) = remoteCall conn (RetrieveDatabaseContextFunctionSummary sessionId)        +databaseContextFunctionsAsRelation sessionId conn@(RemoteConnection _) = remoteCall conn (RetrieveDatabaseContextFunctionSummary sessionId)++notificationsAsRelation :: SessionId -> Connection -> IO (Either RelationalError Relation)+notificationsAsRelation sessionId (InProcessConnection conf) = do+  let sessions = ipSessions conf+  atomically $ do+    eSession <- sessionAndSchema sessionId sessions+    case eSession of+      Left err -> pure (Left err)+      Right (session, schema) ->+        pure (Schema.notificationsAsRelationInSchema (notifications (concreteDatabaseContext session)) schema)+notificationsAsRelation sessionId conn@RemoteConnection{} = remoteCall conn (RetrieveNotificationsAsRelation sessionId)  -- | Returns the transaction id for the connection's disconnected transaction committed parent transaction.   headTransactionId :: SessionId -> Connection -> IO (Either RelationalError TransactionId)
src/lib/ProjectM36/Error.hs view
@@ -84,6 +84,7 @@                      | RelationValuedAttributesNotSupportedError [AttributeName]                      | NotificationNameInUseError NotificationName                      | NotificationNameNotInUseError NotificationName+                     | NotificationValidationError NotificationName NotificationExpression RelationalError                      | ImportError ImportError'                      | ExportError T.Text                      | UnhandledExceptionError String
src/lib/ProjectM36/IsomorphicSchema.hs view
@@ -15,6 +15,7 @@ import qualified Data.Vector as V import qualified ProjectM36.Attribute as A import ProjectM36.AtomType+import Data.Text (Text) #if __GLASGOW_HASKELL__ < 804 import Data.Monoid #endif@@ -415,3 +416,31 @@ instance Morph GraphRefRelationalExpr where -- cannot be supported because we don't track how the schema changes over the lifetime of a transaction graph -}++notificationsAsRelationInSchema :: Notifications -> Schema -> Either RelationalError Relation+notificationsAsRelationInSchema notifs schema  = do+  let attrs = A.attributesFromList [Attribute "name" TextAtomType,+                                    Attribute "changeExpr" RelationalExprAtomType,+                                    Attribute "reportOldExpr" RelationalExprAtomType,+                                    Attribute "reportNewExpr" RelationalExprAtomType]+      relExprT = processRelationalExprInSchema schema+      transform (name, e1, e2, e3) = do+        e1' <- relExprT e1+        e2' <- relExprT e2+        e3' <- relExprT e3+        pure (name, e1', e2', e3')+  notifsData <- mapM transform (notificationsAsData notifs)+  let mkRow (name, changeE, oldE, newE) = [TextAtom name,+                                           RelationalExprAtom changeE,+                                           RelationalExprAtom oldE,+                                           RelationalExprAtom newE]+  mkRelationFromList attrs (map mkRow notifsData)++notificationsAsData :: Notifications -> [(Text, RelationalExpr, RelationalExpr, RelationalExpr)]+notificationsAsData notifs =+    map mkRow (M.toList notifs)+  where+    mkRow (name, notif) = (name,+                           changeExpr notif,+                           reportOldExpr notif,+                           reportNewExpr notif)
src/lib/ProjectM36/RelationalExpression.hs view
@@ -22,7 +22,7 @@ import qualified Data.Map as M import qualified Data.HashSet as HS import qualified Data.Set as S-import Control.Monad (foldM, unless, when)+import Control.Monad (foldM, unless, when, forM_) import Control.Monad.Except (ExceptT, MonadError, runExceptT, throwError, catchError) import Control.Monad.Reader (ReaderT, runReaderT, asks, ask, local) import qualified Control.Monad.Reader as R@@ -382,15 +382,20 @@ -- | Add a notification which will send the resultExpr when triggerExpr changes between commits. evalGraphRefDatabaseContextExpr (AddNotification notName triggerExpr resultOldExpr resultNewExpr) = do   currentContext <- getStateContext+  graph <- dbcGraph+  transId <- dbcTransId   let nots = notifications currentContext   if M.member notName nots then     dbErr (NotificationNameInUseError notName)     else do-      let newNotifications = M.insert notName newNotification nots-          newNotification = Notification { changeExpr = triggerExpr,+      let newNotification = Notification { changeExpr = triggerExpr,                                            reportOldExpr = resultOldExpr,                                             reportNewExpr = resultNewExpr}-      putStateContext $ currentContext { notifications = newNotifications }+          newNotifications = M.insert notName newNotification nots+          potentialContext = currentContext { notifications = newNotifications }+      case checkConstraints potentialContext transId graph of+        Left err -> dbErr err+        Right () -> putStateContext potentialContext    evalGraphRefDatabaseContextExpr (RemoveNotification notName) = do   currentContext <- getStateContext@@ -693,6 +698,7 @@ checkConstraints context transId graph@(TransactionGraph graphHeads transSet) = do   mapM_ (uncurry checkIncDep) (M.toList deps)   mapM_ checkRegisteredQuery (M.toList (registeredQueries context))+  mapM_ checkNotification (M.toList (notifications context))   where     potentialGraph = TransactionGraph graphHeads (S.insert tempTrans transSet)     tempStamp = UTCTime { utctDay = fromGregorian 2000 1 1,@@ -734,6 +740,18 @@       case runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr gfExpr) of         Left err -> Left (RegisteredQueryValidationError qName err)         Right _ -> pure ()+    checkRelExpr relExpr = do+      let gfExpr = process (processRelationalExpr relExpr)+      runGraphRefRelationalExprM gfEnv (typeForGraphRefRelationalExpr gfExpr)+    checkNotification (notName, notif) = do+      forM_ [(NotificationChangeExpression, changeExpr notif),+             (NotificationReportOldExpression, reportOldExpr notif),+             (NotificationReportNewExpression, reportNewExpr notif)] $+        \(typ, relExpr) -> do+          case checkRelExpr relExpr of+            Left err -> Left (NotificationValidationError notName typ err)+            Right _ -> pure ()+        -- the type of a relational expression is equal to the relation attribute set returned from executing the relational expression; therefore, the type can be cheaply derived by evaluating a relational expression and ignoring and tuple processing -- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation@@ -1582,3 +1600,13 @@   where     targetAttrExprs = map NakedAttributeExpr (A.toList targetAttrs)     hint = addTargetTypeHints targetAttrs++-- | Ensure that the notification contains valid, type-checkable relational expressions. These relational expressions therefore become registered queries: queries which must remain valid.+validateNotification :: Notification -> DatabaseContext -> TransactionGraph -> Either RelationalError Notification+validateNotification notif context graph = do+  let reEnv = mkRelationalExprEnv context graph+  runRelationalExprM reEnv $ do+    _ <- typeForRelationalExpr (changeExpr notif)+    _ <- typeForRelationalExpr (reportOldExpr notif)+    _ <- typeForRelationalExpr (reportNewExpr notif)+    pure notif
src/lib/ProjectM36/SQL/Convert.hs view
@@ -1032,7 +1032,7 @@       PrefixOperator _ e1 -> rec' e1       PostfixOperator e1 _ -> rec' e1       BetweenOperator e1 _ e2 -> rec' e1 || rec' e2-      FunctionApplication _ e1 -> or (rec' <$> e1)+      FunctionApplication _ e1 -> any rec' e1       CaseExpr cases else' -> any (\(when', then') ->                                           rec' when' || rec' then' || maybe False rec' else') cases       QuantifiedComparison{} -> True
src/lib/ProjectM36/Serialise/Base.hs view
@@ -49,6 +49,7 @@ deriving via WineryVariant (ExtendTupleExprBase a) instance Serialise a => Serialise (ExtendTupleExprBase a) deriving via WineryVariant Schema instance Serialise Schema deriving via WineryVariant MergeStrategy instance Serialise MergeStrategy+deriving via WineryVariant NotificationExpression instance Serialise NotificationExpression  fromWordsTup :: (Word32, Word32, Word32, Word32) -> TransactionId fromWordsTup (a,b,c,d) = fromWords a b c d
src/lib/ProjectM36/Server.hs view
@@ -117,7 +117,10 @@                         handleConvertSQLQuery ti sessionId conn q),      RequestHandler (\sState (ConvertSQLUpdates sessionId updates) -> do                         conn <- getConn sState-                        handleConvertSQLUpdates ti sessionId conn updates)+                        handleConvertSQLUpdates ti sessionId conn updates),+     RequestHandler (\sState (RetrieveNotificationsAsRelation sessionId) -> do+                        conn <- getConn sState+                        handleRetrieveNotificationsAsRelation ti sessionId conn)      ] ++ if testFlag then testModeHandlers ti else []  getConn :: ConnectionState ServerState -> IO Connection@@ -197,6 +200,7 @@   clientMap <- StmMap.new   StmMap.insert conn dbName dbmap   pure (ServerState { stateDBMap = dbmap, stateClientMap = clientMap })+ -- | A synchronous function to start the project-m36 daemon given an appropriate 'ServerConfig'. Note that this function only returns if the server exits. Returns False if the daemon exited due to an error. If the second argument is not Nothing, the port is put after the server is ready to service the port. launchServer :: ServerConfig -> Maybe (MVar SockAddr) -> IO Bool launchServer daemonConfig mAddr = do@@ -211,22 +215,12 @@           hPutStrLn stderr ("Failed to create database connection: " ++ show err)           pure False         Right conn -> do-          let hostname = bindHost daemonConfig-              port = fromIntegral (bindPort daemonConfig)-+          let mTimeout = fromIntegral <$>+                case perRequestTimeout daemonConfig of+                  0 -> Nothing+                  v -> Just v+          (sockSpec, sockAddr) <- resolveRemoteServerAddress (bindAddress daemonConfig)+          sState <- initialServerState (databaseName daemonConfig) conn+          serve (requestHandlers (testMode daemonConfig) mTimeout) sState sockSpec sockAddr mAddr -          --curryer only supports IPv4 for now-          let addrHints = defaultHints { addrSocketType = Stream, addrFamily = AF_INET }-          hostAddrs <- getAddrInfo (Just addrHints) (Just hostname) Nothing-          case hostAddrs of-            [] -> hPutStrLn stderr ("Failed to resolve: " <> hostname) >> pure False-            (AddrInfo _ _ _ _ (SockAddrInet _ addr32) _):_ -> do-              let hostAddr = hostAddressToTuple addr32-                  mTimeout = fromIntegral <$> case perRequestTimeout daemonConfig of-                                              0 -> Nothing-                                              v -> Just v-                  -              sState <- initialServerState (databaseName daemonConfig) conn-              serve (requestHandlers (testMode daemonConfig) mTimeout) sState hostAddr port mAddr-            _ -> error "unsupported socket addressing mode (IPv4 only currently)" 
src/lib/ProjectM36/Server/Config.hs view
@@ -4,8 +4,7 @@ data ServerConfig = ServerConfig { persistenceStrategy :: PersistenceStrategy,                                    checkFS :: Bool,                                    databaseName :: DatabaseName,-                                   bindHost :: Hostname,-                                   bindPort :: Port,+                                   bindAddress :: RemoteServerAddress,                                    ghcPkgPaths :: [String], -- used for AtomFunction dynamic compilation                                    perRequestTimeout :: Int,                                    testMode :: Bool -- used exclusively for automated testing of the server, thus not accessible from the command line@@ -19,12 +18,12 @@                               deriving (Show)  defaultServerConfig :: ServerConfig-defaultServerConfig = ServerConfig { persistenceStrategy = NoPersistence,-                                     checkFS = True,-                                     databaseName = "base", -                                     bindHost = "127.0.0.1",-                                     bindPort = 6543,-                                     ghcPkgPaths = [],-                                     perRequestTimeout = 0,-                                     testMode = False-                                     }+defaultServerConfig =+  ServerConfig { persistenceStrategy = NoPersistence,+                 checkFS = True,+                 databaseName = "base", +                 bindAddress = RemoteServerHostAddress "127.0.0.1" 6543,+                 ghcPkgPaths = [],+                 perRequestTimeout = 0,+                 testMode = False+               }
src/lib/ProjectM36/Server/EntryPoints.hs view
@@ -165,3 +165,7 @@  handleConvertSQLUpdates :: Maybe Timeout -> SessionId -> Connection -> [DBUpdate] -> IO (Either RelationalError DatabaseContextExpr) handleConvertSQLUpdates ti sessionId conn ups = timeoutRelErr ti (C.convertSQLDBUpdates sessionId conn ups)++handleRetrieveNotificationsAsRelation :: Maybe Timeout -> SessionId -> Connection -> IO (Either RelationalError Relation)+handleRetrieveNotificationsAsRelation ti sessionId conn =+  timeoutRelErr ti (C.notificationsAsRelation sessionId conn)
src/lib/ProjectM36/Server/ParseArgs.hs view
@@ -12,8 +12,7 @@                                  parsePersistenceStrategy <*>                                  parseCheckFS <*>                                  parseDatabaseName <*>-                                 parseHostname (bindHost defaults) <*>-                                 parsePort (bindPort defaults) <*>+                                 parseServerAddress <*>                                  many parseGhcPkgPath <*>                                  parseTimeout (perRequestTimeout defaults) <*>                                  parseTestMode@@ -38,6 +37,19 @@ parseCheckFS :: Parser Bool parseCheckFS = flag True False (long "disable-fscheck" <>                                 help "Disable filesystem check for journaling.")++parseServerAddress :: Parser RemoteServerAddress+parseServerAddress =+  (RemoteServerHostAddress <$>+   parseHostname "127.0.0.1" <*>+   parsePort 6543)+  <|>+  (RemoteServerUnixDomainSocketAddress <$> parseUnixDomainSocketPath)++parseUnixDomainSocketPath :: Parser FilePath+parseUnixDomainSocketPath = strOption (short 'x' <>+                                       long "unix-domain-socket" <>+                                       metavar "SOCKET_PATH")  parseDatabaseName :: Parser DatabaseName parseDatabaseName = strOption (short 'n' <>
src/lib/ProjectM36/Server/RemoteCallTypes.hs view
@@ -76,6 +76,9 @@    data RetrieveAtomTypesAsRelation = RetrieveAtomTypesAsRelation SessionId   RPCData(RetrieveAtomTypesAsRelation)++data RetrieveNotificationsAsRelation = RetrieveNotificationsAsRelation SessionId+  RPCData(RetrieveNotificationsAsRelation)    data RetrieveRelationVariableSummary = RetrieveRelationVariableSummary SessionId   RPCData(RetrieveRelationVariableSummary)
test/Server/Main.hs view
@@ -65,7 +65,7 @@  testConnection :: Port -> MVar () -> IO (Either ConnectionError (SessionId, Connection)) testConnection serverPort mvar = do-  let connInfo = RemoteConnectionInfo testDatabaseName "127.0.0.1" (show serverPort) (testNotificationCallback mvar)+  let connInfo = RemoteConnectionInfo testDatabaseName (RemoteServerHostAddress "127.0.0.1" serverPort) (testNotificationCallback mvar)   --putStrLn ("testConnection: " ++ show serverAddress)   eConn <- connectProjectM36 connInfo   case eConn of @@ -86,7 +86,7 @@                                          persistenceStrategy = CrashSafePersistence (tempdir </> "db"),                                          perRequestTimeout = ti,                                          testMode = True,-                                         bindPort = 0,+                                         bindAddress = RemoteServerHostAddress "127.0.0.1" 0,                                          checkFS = False --not stricly needed for these tests                                        }     @@ -242,7 +242,7 @@  testClientConnectFail :: Test testClientConnectFail = TestCase $ do-  let connInfo = RemoteConnectionInfo "nonexistentdb" "127.0.0.1" "7777" emptyNotificationCallback+  let connInfo = RemoteConnectionInfo "nonexistentdb" (RemoteServerHostAddress "127.0.0.1" 7777) emptyNotificationCallback   eConn <- connectProjectM36 connInfo   case eConn of     Left (IOExceptionError _) -> pure ()
test/Server/WebSocket.hs view
@@ -26,7 +26,7 @@ launchTestServer = do   addressMVar <- newEmptyMVar   let config = defaultServerConfig { databaseName = testDatabaseName, -                                     bindPort = 0,+                                     bindAddress = RemoteServerHostAddress "127.0.0.1" 0,                                      checkFS = False}       testDatabaseName = "test"   -- start normal server
test/TutorialD/InterpreterTest.hs view
@@ -54,7 +54,6 @@       transactionJumpTest,        transactionBranchTest,        simpleJoinTest, -      testNotification,       testTypeConstructors,        testMergeTransactions,        testComments, @@ -96,7 +95,9 @@       testRegisteredQueries,       testCrossJoin,       testIfThenExpr,-      testSubrelationAttributeAtomExpr+      testSubrelationAttributeAtomExpr,+      testComplexTypeVarResolution,+      testNotifications       ]  simpleRelTests :: Test@@ -335,8 +336,8 @@ -}                             -- test notifications over the InProcessConnection-testNotification :: Test-testNotification = TestCase $ do+testNotifications :: Test+testNotifications = TestCase $ do   notifmvar <- newEmptyMVar   let notifCallback mvar _ _ = putMVar mvar ()       relvarx = RelationVariable "x" ()@@ -347,7 +348,17 @@   executeDatabaseContextExpr sess conn (Assign "x" (ExistingRelation relationFalse)) >>= eitherFail   commit sess conn >>= eitherFail   takeMVar notifmvar-    ++  -- test documented behavior+  let notifCallback2 name _evald = +        assertEqual "notification" "steve_change" name+  (session, dbconn) <- dateExamplesConnection notifCallback2+  executeTutorialD session dbconn "person:=relation{tuple{name \"Steve\",address \"Main St.\"},tuple{name \"John\", address \"Elm St.\"}}"+  executeTutorialD session dbconn "notify steve_change person where name=\"Steve\" true (person where name=\"Steve\"){address}"+  _ <- commit session dbconn+  executeTutorialD session dbconn "update person where name=\"Steve\" (address:=\"Grove St.\")"+  expectTutorialDErr session dbconn (T.isPrefixOf "NotificationValidationError") "undefine person"+       testTypeConstructors :: Test testTypeConstructors = TestCase $ do   (sessionId, dbconn) <- dateExamplesConnection emptyNotificationCallback@@ -886,3 +897,32 @@   executeTutorialD session dbconn "z:= x = y"   res <- executeRelationalExpr session dbconn (RelationVariable "z" ())   assertEqual "sum" (Right relationTrue) res++testComplexTypeVarResolution :: Test+testComplexTypeVarResolution = TestCase $ do+  (session, dbconn) <- dateExamplesConnection emptyNotificationCallback+  executeTutorialD session dbconn "data F = F (Maybe Integer) (Maybe Integer)"+  executeTutorialD session dbconn "x:=relation{x F}{tuple{x F Nothing (Just 4)}}"+  executeTutorialD session dbconn "y:=relation{x F}{tuple{x F Nothing Nothing}}"+  +  resX <- executeRelationalExpr session dbconn (RelationVariable "x" ())+  resY <- executeRelationalExpr session dbconn (RelationVariable "y" ())++  +  let expectedX = mkRelationFromList attrs+        [[ConstructedAtom "F"+         (ConstructedAtomType "F" mempty)+          [ConstructedAtom "Nothing" mint [],+            ConstructedAtom "Just" mint [IntegerAtom 4]]]]+      attrs = A.attributesFromList [Attribute "x" (ConstructedAtomType "F" mempty)]+      mint = ConstructedAtomType "Maybe" (M.singleton "a" IntegerAtomType)+  assertEqual "type var resolution x" expectedX resX++  let expectedY = mkRelationFromList attrs+                  [[ConstructedAtom "F"+                    (ConstructedAtomType "F" mempty)+                    [ConstructedAtom "Nothing" mint [],+                     ConstructedAtom "Nothing" mint []]]]+ +  assertEqual "type var resolution y" expectedY resY+