{-# LANGUAGE RankNTypes #-}{-# LANGUAGE EmptyDataDecls #-}{-# LANGUAGE TypeFamilies #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE MultiParamTypeClasses #-}{-# LANGUAGE UndecidableInstances #-}{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE DeriveDataTypeable #-}moduleDatabase.Persist.Sql.TypeswhereimportControl.Exception(Exception)importControl.Monad.Trans.Resource(MonadResource(..),MonadThrow(..),ResourceT)importControl.Monad.Logger(MonadLogger(..),NoLoggingT)importControl.Monad.Trans.ControlimportControl.Monad.Trans.Class(MonadTrans(..))importControl.Monad.IO.Class(MonadIO(..))importControl.Monad.Trans.Reader(ReaderT(..))importControl.Applicative(Applicative(..))importControl.Monad.Trans.Writer(WriterT)importControl.Monad.Base(MonadBase(..))importControl.Monad(MonadPlus(..))importData.Typeable(Typeable)importControl.Monad(liftM)importDatabase.Persist.TypesimportData.Text(Text,pack)importqualifiedData.TextasTimportData.IORef(IORef)importData.Map(Map)importData.Int(Int64)importData.Conduit(Source)importData.Pool(Pool)importWeb.PathPiecesimportControl.Exception(throw)importqualifiedData.Text.ReaddataInsertSqlResult=ISRSingleText|ISRInsertGetTextText|ISRManyKeysText[PersistValue]dataConnection=Connection{connPrepare::Text->IOStatement-- | table name, column names, id name, either 1 or 2 statements to run,connInsertSql::EntityDefSqlType->[PersistValue]->InsertSqlResult,connStmtMap::IORef(MapTextStatement),connClose::IO(),connMigrateSql::[EntityDefSqlType]->(Text->IOStatement)->EntityDefSqlType->IO(Either[Text][(Bool,Text)]),connBegin::(Text->IOStatement)->IO(),connCommit::(Text->IOStatement)->IO(),connRollback::(Text->IOStatement)->IO(),connEscapeName::DBName->Text,connNoLimit::Text,connRDBMS::Text,connLimitOffset::(Int,Int)->Bool->Text->Text}dataStatement=Statement{stmtFinalize::IO(),stmtReset::IO(),stmtExecute::[PersistValue]->IOInt64,stmtQuery::forallm.MonadResourcem=>[PersistValue]->Sourcem[PersistValue]}dataColumn=Column{cName::!DBName,cNull::!Bool,cSqlType::!SqlType,cDefault::!(MaybeText),cDefaultConstraintName::!(MaybeDBName),cMaxLen::!(MaybeInteger),cReference::!(Maybe(DBName,DBName))-- table name, constraint name}deriving(Eq,Ord,Show)dataPersistentSqlException=StatementAlreadyFinalizedText|Couldn'tGetSQLConnectionderiving(Typeable,Show)instanceExceptionPersistentSqlExceptiondataSqlBackendderivingTypeablenewtypeSqlPersistTma=SqlPersistT{unSqlPersistT::ReaderTConnectionma}deriving(Monad,MonadIO,MonadTrans,Functor,Applicative,MonadPlus)typeSqlPersist=SqlPersistT{-# DEPRECATED SqlPersist "Please use SqlPersistT instead" #-}typeSqlPersistM=SqlPersistT(NoLoggingT(ResourceTIO))instanceMonadThrowm=>MonadThrow(SqlPersistTm)wheremonadThrow=lift.monadThrowinstanceMonadBasebackendm=>MonadBasebackend(SqlPersistTm)whereliftBase=lift.liftBaseinstanceMonadBaseControlbackendm=>MonadBaseControlbackend(SqlPersistTm)wherenewtypeStM(SqlPersistTm)a=StMSP{unStMSP::ComposeStSqlPersistTma}liftBaseWith=defaultLiftBaseWithStMSPrestoreM=defaultRestoreMunStMSPinstanceMonadTransControlSqlPersistTwherenewtypeStTSqlPersistTa=StReader{unStReader::a}liftWithf=SqlPersistT$ReaderT$\r->f$\t->liftMStReader$runReaderT(unSqlPersistTt)rrestoreT=SqlPersistT.ReaderT.const.liftMunStReaderinstanceMonadResourcem=>MonadResource(SqlPersistTm)whereliftResourceT=lift.liftResourceTinstanceMonadLoggerm=>MonadLogger(SqlPersistTm)wheremonadLoggerLogabc=lift.monadLoggerLogabctypeSql=Text-- Bool indicates if the Sql is safetypeCautiousMigration=[(Bool,Sql)]typeMigrationm=WriterT[Text](WriterTCautiousMigrationm)()typeConnectionPool=PoolConnectioninstancePathPiece(KeyBackendSqlBackendentity)wheretoPathPiece(Key(PersistInt64i))=toPathPieceitoPathPiecek=throw$PersistInvalidField$pack$"Invalid Key: "++showkfromPathPiecet=caseData.Text.Read.signedData.Text.Read.decimaltofRight(i,t')|T.nullt'->Just$Key$PersistInt64i_->Nothing-- $rawSql---- Although it covers most of the useful cases, @persistent@'s-- API may not be enough for some of your tasks. May be you need-- some complex @JOIN@ query, or a database-specific command-- needs to be issued.---- To issue raw SQL queries you could use 'R.withStmt', which-- allows you to do anything you need. However, its API is-- /low-level/ and you need to parse each row yourself. However,-- most of your complex queries will have simple results -- some-- of your entities and maybe a couple of derived columns.---- This is where 'rawSql' comes in. Like 'R.withStmt', you may-- issue /any/ SQL query. However, it does all the hard work for-- you and automatically parses the rows of the result. It may-- return:---- * An 'Entity', that which 'selectList' returns.-- All of your entity's fields are-- automatically parsed.---- * A @'Single' a@, which is a single, raw column of type @a@.-- You may use a Haskell type (such as in your entity-- definitions), for example @Single Text@ or @Single Int@,-- or you may get the raw column value with @Single-- 'PersistValue'@.---- * A tuple combining any of these (including other tuples).-- Using tuples allows you to return many entities in one-- query.---- The only difference between issuing SQL queries with 'rawSql'-- and using other means is that we have an /entity selection/-- /placeholder/, the double question mark @??@. It /must/ be-- used whenever you want to @SELECT@ an 'Entity' from your-- query. Here's a sample SQL query @sampleStmt@ that may be-- issued:---- @-- SELECT ??, ??-- FROM \"Person\", \"Likes\", \"Object\"-- WHERE \"Person\".id = \"Likes\".\"personId\"-- AND \"Object\".id = \"Likes\".\"objectId\"-- AND \"Person\".name LIKE ?-- @---- To use that query, you could say---- @-- do results <- 'rawSql' sampleStmt [\"%Luke%\"]-- forM_ results $-- \\( Entity personKey person-- , Entity objectKey object-- ) -> do ...-- @---- Note that 'rawSql' knows how to replace the double question-- marks @??@ because of the type of the @results@.-- | A single column (see 'rawSql'). Any 'PersistField' may be-- used here, including 'PersistValue' (which does not do any-- processing).newtypeSinglea=Single{unSingle::a}deriving(Eq,Ord,Show,Read)