{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
module Database.Beam.Query
(
module Database.Beam.Query.Types
, QAggregateContext, QGroupingContext, QValueContext
, QWindowingContext, QWindowFrameContext
, QueryableSqlSyntax
, QGenExprTable, QExprTable
, module Database.Beam.Query.Combinators
, module Database.Beam.Query.Extensions
, module Database.Beam.Query.Relationships
, module Database.Beam.Query.Operator
, Beam.SqlBool
, isTrue_, isNotTrue_
, isFalse_, isNotFalse_
, isUnknown_, isNotUnknown_
, unknownAs_, sqlBool_
, HasSqlEqualityCheck(..), HasSqlQuantifiedEqualityCheck(..)
, SqlEq(..), SqlOrd(..)
, SqlEqQuantified(..), SqlOrdQuantified(..)
, QQuantified
, anyOf_, allOf_, anyIn_, allIn_
, between_
, in_
, module Database.Beam.Query.Aggregate
, module Database.Beam.Query.CustomSQL
, SqlSelect(..)
, select, lookup_
, runSelectReturningList
, runSelectReturningOne
, dumpSqlSelect
, SqlInsert(..)
, insert, insertOnly
, runInsert
, SqlInsertValues(..)
, insertExpressions
, insertValues
, insertFrom
, insertData
, SqlUpdate(..)
, update, save
, runUpdate
, SqlDelete(..)
, delete
, runDelete ) where
import Prelude hiding (lookup)
import Database.Beam.Query.Aggregate
import Database.Beam.Query.Combinators
import Database.Beam.Query.CustomSQL
import Database.Beam.Query.Extensions
import Database.Beam.Query.Internal
import Database.Beam.Query.Operator hiding (SqlBool)
import qualified Database.Beam.Query.Operator as Beam
import Database.Beam.Query.Ord
import Database.Beam.Query.Relationships
import Database.Beam.Query.Types (QGenExpr)
import Database.Beam.Query.Types hiding (QGenExpr)
import Database.Beam.Backend.Types
import Database.Beam.Backend.SQL
import Database.Beam.Backend.SQL.Builder
import Database.Beam.Schema.Tables
import Control.Monad.Identity
import Control.Monad.Writer
import Data.Text (Text)
import Data.Proxy
data QueryInaccessible
type QGenExprTable ctxt syntax s tbl = tbl (QGenExpr ctxt syntax s)
type QExprTable syntax s tbl = QGenExprTable QValueContext syntax s tbl
newtype SqlSelect select a
= SqlSelect select
type QueryableSqlSyntax cmd =
( IsSql92Syntax cmd
, Sql92SanityCheck cmd
, HasQBuilder (Sql92SelectSyntax cmd) )
select :: forall syntax db res.
( ProjectibleInSelectSyntax syntax res
, IsSql92SelectSyntax syntax
, HasQBuilder syntax ) =>
Q syntax db QueryInaccessible res -> SqlSelect syntax (QExprToIdentity res)
select q =
SqlSelect (buildSqlQuery "t" q)
lookup_ :: ( HasQBuilder syntax
, Sql92SelectSanityCheck syntax
, SqlValableTable (PrimaryKey table) (Sql92SelectExpressionSyntax syntax)
, HasSqlValueSyntax (Sql92ExpressionValueSyntax (Sql92SelectExpressionSyntax syntax)) Bool
, HasTableEquality (Sql92SelectExpressionSyntax syntax) (PrimaryKey table)
, Beamable table, Table table
, Database be db )
=> DatabaseEntity be db (TableEntity table)
-> PrimaryKey table Identity
-> SqlSelect syntax (table Identity)
lookup_ tbl tblKey =
select $
filter_ (\t -> pk t ==. val_ tblKey) $
all_ tbl
runSelectReturningList ::
(IsSql92Syntax cmd, MonadBeam cmd be hdl m, FromBackendRow be a) =>
SqlSelect (Sql92SelectSyntax cmd) a -> m [ a ]
runSelectReturningList (SqlSelect s) =
runReturningList (selectCmd s)
runSelectReturningOne ::
(IsSql92Syntax cmd, MonadBeam cmd be hdl m, FromBackendRow be a) =>
SqlSelect (Sql92SelectSyntax cmd) a -> m (Maybe a)
runSelectReturningOne (SqlSelect s) =
runReturningOne (selectCmd s)
dumpSqlSelect :: ProjectibleInSelectSyntax SqlSyntaxBuilder res =>
Q SqlSyntaxBuilder db QueryInaccessible res -> IO ()
dumpSqlSelect q =
let SqlSelect s = select q
in putStrLn (renderSql s)
data SqlInsert syntax
= SqlInsert syntax
| SqlInsertNoRows
insertOnly :: ( IsSql92InsertSyntax syntax, Projectible Text (QExprToField r) )
=> DatabaseEntity be db (TableEntity table)
-> (table (QField s) -> QExprToField r)
-> SqlInsertValues (Sql92InsertValuesSyntax syntax) r
-> SqlInsert syntax
insertOnly _ _ SqlInsertValuesEmpty = SqlInsertNoRows
insertOnly (DatabaseEntity (DatabaseTable tblNm tblSettings)) mkProj (SqlInsertValues vs) =
SqlInsert (insertStmt tblNm proj vs)
where
tblFields = changeBeamRep (\(Columnar' (TableField name)) -> Columnar' (QField False tblNm name)) tblSettings
proj = execWriter (project' (Proxy @AnyType) (\_ f -> tell [f ""] >> pure f)
(mkProj tblFields))
insert :: ( IsSql92InsertSyntax syntax, Projectible Text (table (QField s)) )
=> DatabaseEntity be db (TableEntity table)
-> SqlInsertValues (Sql92InsertValuesSyntax syntax) (table (QExpr (Sql92InsertExpressionSyntax syntax) s))
-> SqlInsert syntax
insert tbl values = insertOnly tbl id values
runInsert :: (IsSql92Syntax cmd, MonadBeam cmd be hdl m)
=> SqlInsert (Sql92InsertSyntax cmd) -> m ()
runInsert SqlInsertNoRows = pure ()
runInsert (SqlInsert i) = runNoReturn (insertCmd i)
data SqlInsertValues insertValues proj
= SqlInsertValues insertValues
| SqlInsertValuesEmpty
insertExpressions ::
forall syntax table s.
( Beamable table
, IsSql92InsertValuesSyntax syntax ) =>
(forall s'. [ table (QExpr (Sql92InsertValuesExpressionSyntax syntax) s') ]) ->
SqlInsertValues syntax (table (QExpr (Sql92InsertValuesExpressionSyntax syntax) s))
insertExpressions tbls =
case sqlExprs of
[] -> SqlInsertValuesEmpty
_ -> SqlInsertValues (insertSqlExpressions sqlExprs)
where
sqlExprs = map mkSqlExprs tbls
mkSqlExprs :: forall s'. table (QExpr (Sql92InsertValuesExpressionSyntax syntax) s') -> [Sql92InsertValuesExpressionSyntax syntax]
mkSqlExprs = allBeamValues (\(Columnar' (QExpr x)) -> x "t")
insertValues ::
forall table syntax s.
( Beamable table
, IsSql92InsertValuesSyntax syntax
, FieldsFulfillConstraint (HasSqlValueSyntax (Sql92ExpressionValueSyntax (Sql92InsertValuesExpressionSyntax syntax))) table) =>
[ table Identity ] -> SqlInsertValues syntax (table (QExpr (Sql92InsertValuesExpressionSyntax syntax) s))
insertValues x = insertExpressions (map val_ x :: forall s'. [table (QExpr (Sql92InsertValuesExpressionSyntax syntax) s') ])
insertData :: forall syntax r
. ( Projectible (Sql92InsertValuesExpressionSyntax syntax) r
, IsSql92InsertValuesSyntax syntax )
=> [ r ] -> SqlInsertValues syntax r
insertData rows =
case rows of
[] -> SqlInsertValuesEmpty
_ -> SqlInsertValues (insertSqlExpressions (map mkSqlExprs rows))
where
mkSqlExprs :: r -> [Sql92InsertValuesExpressionSyntax syntax]
mkSqlExprs r = execWriter (project' (Proxy @AnyType) (\_ s -> tell [ s "t" ] >> pure s) r)
insertFrom
:: ( IsSql92InsertValuesSyntax syntax
, HasQBuilder (Sql92InsertValuesSelectSyntax syntax)
, Projectible (Sql92SelectExpressionSyntax (Sql92InsertValuesSelectSyntax syntax)) r )
=> Q (Sql92InsertValuesSelectSyntax syntax) db QueryInaccessible r
-> SqlInsertValues syntax r
insertFrom s = SqlInsertValues (insertFromSql (buildSqlQuery "t" s))
data SqlUpdate syntax (table :: (* -> *) -> *)
= SqlUpdate syntax
| SqlIdentityUpdate
update :: ( Beamable table
, IsSql92UpdateSyntax syntax) =>
DatabaseEntity be db (TableEntity table)
-> (forall s. table (QField s) -> [ QAssignment (Sql92UpdateFieldNameSyntax syntax) (Sql92UpdateExpressionSyntax syntax) s ])
-> (forall s. table (QExpr (Sql92UpdateExpressionSyntax syntax) s) -> QExpr (Sql92UpdateExpressionSyntax syntax) s Bool)
-> SqlUpdate syntax table
update (DatabaseEntity (DatabaseTable tblNm tblSettings)) mkAssignments mkWhere =
case assignments of
[] -> SqlIdentityUpdate
_ -> SqlUpdate (updateStmt tblNm assignments (Just (where_ "t")))
where
assignments = concatMap (\(QAssignment as) -> as) (mkAssignments tblFields)
QExpr where_ = mkWhere tblFieldExprs
tblFields = changeBeamRep (\(Columnar' (TableField name)) -> Columnar' (QField False tblNm name)) tblSettings
tblFieldExprs = changeBeamRep (\(Columnar' (QField _ _ nm)) -> Columnar' (QExpr (pure (fieldE (unqualifiedField nm))))) tblFields
save :: forall table syntax be db.
( Table table
, IsSql92UpdateSyntax syntax
, SqlValableTable (PrimaryKey table) (Sql92UpdateExpressionSyntax syntax)
, SqlValableTable table (Sql92UpdateExpressionSyntax syntax)
, HasTableEquality (Sql92UpdateExpressionSyntax syntax) (PrimaryKey table)
, HasSqlValueSyntax (Sql92ExpressionValueSyntax (Sql92UpdateExpressionSyntax syntax)) Bool
)
=> DatabaseEntity be db (TableEntity table)
-> table Identity
-> SqlUpdate syntax table
save tbl@(DatabaseEntity (DatabaseTable _ tblSettings)) v =
update tbl (\(tblField :: table (QField s)) ->
execWriter $
zipBeamFieldsM
(\(Columnar' field) c@(Columnar' value) ->
do when (qFieldName field `notElem` primaryKeyFieldNames) $
tell [ field <-. value ]
pure c)
tblField (val_ v :: table (QExpr (Sql92UpdateExpressionSyntax syntax) s)))
(\tblE -> primaryKey tblE ==. val_ (primaryKey v))
where
primaryKeyFieldNames =
allBeamValues (\(Columnar' (TableField fieldNm)) -> fieldNm) (primaryKey tblSettings)
runUpdate :: (IsSql92Syntax cmd, MonadBeam cmd be hdl m)
=> SqlUpdate (Sql92UpdateSyntax cmd) tbl -> m ()
runUpdate (SqlUpdate u) = runNoReturn (updateCmd u)
runUpdate SqlIdentityUpdate = pure ()
newtype SqlDelete syntax (table :: (* -> *) -> *) = SqlDelete syntax
delete :: forall be db delete table
. IsSql92DeleteSyntax delete
=> DatabaseEntity be db (TableEntity table)
-> (forall s. (forall s'. table (QExpr (Sql92DeleteExpressionSyntax delete) s')) -> QExpr (Sql92DeleteExpressionSyntax delete) s Bool)
-> SqlDelete delete table
delete (DatabaseEntity (DatabaseTable tblNm tblSettings)) mkWhere =
SqlDelete (deleteStmt tblNm alias (Just (where_ "t")))
where
supportsAlias = deleteSupportsAlias (Proxy @delete)
tgtName = "delete_target"
alias = if supportsAlias then Just tgtName else Nothing
mkField = if supportsAlias then qualifiedField tgtName else unqualifiedField
QExpr where_ = mkWhere (changeBeamRep (\(Columnar' (TableField name)) -> Columnar' (QExpr (pure (fieldE (mkField name))))) tblSettings)
runDelete :: (IsSql92Syntax cmd, MonadBeam cmd be hdl m)
=> SqlDelete (Sql92DeleteSyntax cmd) table -> m ()
runDelete (SqlDelete d) = runNoReturn (deleteCmd d)