Skip to content

Commit

Permalink
Added Table record instance
Browse files Browse the repository at this point in the history
This new instance will be used for further simplifications of the database APIs
  • Loading branch information
mpscholten committed Sep 27, 2021
1 parent 4a647c6 commit 5fff5f6
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 85 deletions.
1 change: 1 addition & 0 deletions IHP/AuthSupport/Controller/Sessions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ createSessionAction :: forall record action passwordField.
, CanUpdate record
, Show (PrimaryKey (GetTableName record))
, record ~ GetModelByTableName (GetTableName record)
, Table record
) => IO ()
createSessionAction = do
usersQueryBuilder
Expand Down
48 changes: 24 additions & 24 deletions IHP/Fetch.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,84 +36,84 @@ import IHP.QueryBuilder

class Fetchable fetchable model | fetchable -> model where
type FetchResult fetchable model
fetch :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => fetchable -> IO (FetchResult fetchable model)
fetchOneOrNothing :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => fetchable -> IO (Maybe model)
fetchOne :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => fetchable -> IO model
fetch :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => fetchable -> IO (FetchResult fetchable model)
fetchOneOrNothing :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => fetchable -> IO (Maybe model)
fetchOne :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => fetchable -> IO model

-- The instance declaration had to be split up because a type variable ranging over HasQueryBuilder instances is not allowed in the declaration of the associated type. The common*-functions reduce the redundancy to the necessary minimum.
instance (model ~ GetModelByTableName table, KnownSymbol table) => Fetchable (QueryBuilder table) model where
type instance FetchResult (QueryBuilder table) model = [model]
{-# INLINE fetch #-}
fetch :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => QueryBuilder table -> IO [model]
fetch :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => QueryBuilder table -> IO [model]
fetch = commonFetch

{-# INLINE fetchOneOrNothing #-}
fetchOneOrNothing :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => QueryBuilder table -> IO (Maybe model)
fetchOneOrNothing :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => QueryBuilder table -> IO (Maybe model)
fetchOneOrNothing = commonFetchOneOrNothing

{-# INLINE fetchOne #-}
fetchOne :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => QueryBuilder table -> IO model
fetchOne :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => QueryBuilder table -> IO model
fetchOne = commonFetchOne

instance (model ~ GetModelByTableName table, KnownSymbol table) => Fetchable (JoinQueryBuilderWrapper r table) model where
type instance FetchResult (JoinQueryBuilderWrapper r table) model = [model]
{-# INLINE fetch #-}
fetch :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => JoinQueryBuilderWrapper r table -> IO [model]
fetch :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => JoinQueryBuilderWrapper r table -> IO [model]
fetch = commonFetch

{-# INLINE fetchOneOrNothing #-}
fetchOneOrNothing :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => JoinQueryBuilderWrapper r table -> IO (Maybe model)
fetchOneOrNothing :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => JoinQueryBuilderWrapper r table -> IO (Maybe model)
fetchOneOrNothing = commonFetchOneOrNothing

{-# INLINE fetchOne #-}
fetchOne :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => JoinQueryBuilderWrapper r table -> IO model
fetchOne :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => JoinQueryBuilderWrapper r table -> IO model
fetchOne = commonFetchOne

instance (model ~ GetModelByTableName table, KnownSymbol table) => Fetchable (NoJoinQueryBuilderWrapper table) model where
type instance FetchResult (NoJoinQueryBuilderWrapper table) model = [model]
{-# INLINE fetch #-}
fetch :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => NoJoinQueryBuilderWrapper table -> IO [model]
fetch :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => NoJoinQueryBuilderWrapper table -> IO [model]
fetch = commonFetch

{-# INLINE fetchOneOrNothing #-}
fetchOneOrNothing :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => NoJoinQueryBuilderWrapper table -> IO (Maybe model)
fetchOneOrNothing :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => NoJoinQueryBuilderWrapper table -> IO (Maybe model)
fetchOneOrNothing = commonFetchOneOrNothing

{-# INLINE fetchOne #-}
fetchOne :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => NoJoinQueryBuilderWrapper table -> IO model
fetchOne :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => NoJoinQueryBuilderWrapper table -> IO model
fetchOne = commonFetchOne

instance (model ~ GetModelByTableName table, KnownSymbol table, FromField value, KnownSymbol foreignTable, foreignModel ~ GetModelByTableName foreignTable, KnownSymbol columnName, HasField columnName foreignModel value, HasQueryBuilder (LabeledQueryBuilderWrapper foreignTable columnName value) NoJoins) => Fetchable (LabeledQueryBuilderWrapper foreignTable columnName value table) model where
type instance FetchResult (LabeledQueryBuilderWrapper foreignTable columnName value table) model = [LabeledData value model]
-- fetch needs to return a list of labeled data. The
{-# INLINE fetch #-}
fetch :: (KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => LabeledQueryBuilderWrapper foreignTable columnName value table -> IO [LabeledData value model]
fetch :: (Table model, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => LabeledQueryBuilderWrapper foreignTable columnName value table -> IO [LabeledData value model]
fetch !queryBuilderProvider = do
let !(theQuery, theParameters) = queryBuilderProvider
|> toSQL
trackTableRead (tableNameByteString @model)
sqlQuery @_ @(LabeledData value model) (Query $ cs theQuery) theParameters

{-# INLINE fetchOneOrNothing #-}
fetchOneOrNothing :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => LabeledQueryBuilderWrapper foreignTable columnName value table -> IO (Maybe model)
fetchOneOrNothing :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => LabeledQueryBuilderWrapper foreignTable columnName value table -> IO (Maybe model)
fetchOneOrNothing = commonFetchOneOrNothing

{-# INLINE fetchOne #-}
fetchOne :: (?modelContext :: ModelContext) => (PG.FromRow model, KnownSymbol (GetTableName model)) => LabeledQueryBuilderWrapper foreignTable columnName value table -> IO model
fetchOne :: (?modelContext :: ModelContext) => (Table model, PG.FromRow model, KnownSymbol (GetTableName model)) => LabeledQueryBuilderWrapper foreignTable columnName value table -> IO model
fetchOne = commonFetchOne



{-# INLINE commonFetch #-}
commonFetch :: forall model table queryBuilderProvider joinRegister.(HasQueryBuilder queryBuilderProvider joinRegister, model ~ GetModelByTableName table, KnownSymbol table, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => queryBuilderProvider table -> IO [model]
commonFetch :: forall model table queryBuilderProvider joinRegister.(Table model, HasQueryBuilder queryBuilderProvider joinRegister, model ~ GetModelByTableName table, KnownSymbol table, KnownSymbol (GetTableName model), PG.FromRow model, ?modelContext :: ModelContext) => queryBuilderProvider table -> IO [model]
commonFetch !queryBuilder = do
let !(theQuery, theParameters) = queryBuilder
|> toSQL
trackTableRead (tableNameByteString @model)
sqlQuery (Query $ cs theQuery) theParameters

{-# INLINE commonFetchOneOrNothing #-}
commonFetchOneOrNothing :: forall model table queryBuilderProvider joinRegister.(?modelContext :: ModelContext) => (KnownSymbol table, HasQueryBuilder queryBuilderProvider joinRegister, PG.FromRow model, KnownSymbol (GetTableName model)) => queryBuilderProvider table -> IO (Maybe model)
commonFetchOneOrNothing :: forall model table queryBuilderProvider joinRegister.(?modelContext :: ModelContext) => (Table model, KnownSymbol table, HasQueryBuilder queryBuilderProvider joinRegister, PG.FromRow model, KnownSymbol (GetTableName model)) => queryBuilderProvider table -> IO (Maybe model)
commonFetchOneOrNothing !queryBuilder = do
let !(theQuery, theParameters) = queryBuilder
|> buildQuery
Expand All @@ -124,7 +124,7 @@ commonFetchOneOrNothing !queryBuilder = do
pure $ listToMaybe results

{-# INLINE commonFetchOne #-}
commonFetchOne :: forall model table queryBuilderProvider joinRegister.(?modelContext :: ModelContext) => (KnownSymbol table, Fetchable (queryBuilderProvider table) model, HasQueryBuilder queryBuilderProvider joinRegister, PG.FromRow model, KnownSymbol (GetTableName model)) => queryBuilderProvider table -> IO model
commonFetchOne :: forall model table queryBuilderProvider joinRegister.(?modelContext :: ModelContext) => (Table model, KnownSymbol table, Fetchable (queryBuilderProvider table) model, HasQueryBuilder queryBuilderProvider joinRegister, PG.FromRow model, KnownSymbol (GetTableName model)) => queryBuilderProvider table -> IO model
commonFetchOne !queryBuilder = do
maybeModel <- fetchOneOrNothing queryBuilder
case maybeModel of
Expand Down Expand Up @@ -174,27 +174,27 @@ fetchExists !queryBuilder = do
{-# INLINE fetchExists #-}

{-# INLINE genericFetchId #-}
genericFetchId :: forall table model. (KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table) => Id' table -> IO [model]
genericFetchId :: forall table model. (Table model, KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table) => Id' table -> IO [model]
genericFetchId !id = query @model |> filterWhereId id |> fetch

{-# INLINE genericfetchIdOneOrNothing #-}
genericfetchIdOneOrNothing :: forall table model. (KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table) => Id' table -> IO (Maybe model)
genericfetchIdOneOrNothing :: forall table model. (Table model, KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table) => Id' table -> IO (Maybe model)
genericfetchIdOneOrNothing !id = query @model |> filterWhereId id |> fetchOneOrNothing

{-# INLINE genericFetchIdOne #-}
genericFetchIdOne :: forall table model. (KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table) => Id' table -> IO model
genericFetchIdOne :: forall table model. (Table model, KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table) => Id' table -> IO model
genericFetchIdOne !id = query @model |> filterWhereId id |> fetchOne

{-# INLINE genericFetchIds #-}
genericFetchIds :: forall table model value. (KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, ToField value, EqOrIsOperator value, HasField "id" model value, model ~ GetModelByTableName table, GetTableName model ~ table) => [value] -> IO [model]
genericFetchIds :: forall table model value. (Table model, KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, ToField value, EqOrIsOperator value, HasField "id" model value, model ~ GetModelByTableName table, GetTableName model ~ table) => [value] -> IO [model]
genericFetchIds !ids = query @model |> filterWhereIn (#id, ids) |> fetch

{-# INLINE genericfetchIdsOneOrNothing #-}
genericfetchIdsOneOrNothing :: forall model value table. (KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, ToField value, EqOrIsOperator value, HasField "id" model value, model ~ GetModelByTableName table, GetTableName model ~ table) => [value] -> IO (Maybe model)
genericfetchIdsOneOrNothing :: forall model value table. (Table model, KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, ToField value, EqOrIsOperator value, HasField "id" model value, model ~ GetModelByTableName table, GetTableName model ~ table) => [value] -> IO (Maybe model)
genericfetchIdsOneOrNothing !ids = query @model |> filterWhereIn (#id, ids) |> fetchOneOrNothing

{-# INLINE genericFetchIdsOne #-}
genericFetchIdsOne :: forall model value table. (KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, ToField value, EqOrIsOperator value, HasField "id" model value, model ~ GetModelByTableName table, GetTableName model ~ table) => [value] -> IO model
genericFetchIdsOne :: forall model value table. (Table model, KnownSymbol table, PG.FromRow model, ?modelContext :: ModelContext, ToField value, EqOrIsOperator value, HasField "id" model value, model ~ GetModelByTableName table, GetTableName model ~ table) => [value] -> IO model
genericFetchIdsOne !ids = query @model |> filterWhereIn (#id, ids) |> fetchOne

{-# INLINE findBy #-}
Expand Down
18 changes: 12 additions & 6 deletions IHP/FetchRelated.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module IHP.FetchRelated (fetchRelated, collectionFetchRelated, collectionFetchRe
import IHP.Prelude
import Database.PostgreSQL.Simple.ToField
import qualified Database.PostgreSQL.Simple as PG
import IHP.ModelSupport (Include, Id', PrimaryKey, GetModelByTableName)
import IHP.ModelSupport (Include, Id', PrimaryKey, GetModelByTableName, Table)
import IHP.QueryBuilder
import IHP.Fetch

Expand Down Expand Up @@ -71,6 +71,7 @@ instance (
, Show (PrimaryKey tableName)
, HasField "id" relatedModel (Id' tableName)
, relatedModel ~ GetModelByTableName (GetTableName relatedModel)
, Table relatedModel
) => CollectionFetchRelated (Id' tableName) relatedModel where
collectionFetchRelated :: forall model relatedField. (
?modelContext :: ModelContext,
Expand All @@ -79,7 +80,8 @@ instance (
Fetchable (Id' tableName) relatedModel,
KnownSymbol (GetTableName relatedModel),
PG.FromRow relatedModel,
KnownSymbol relatedField
KnownSymbol relatedField,
Table relatedModel
) => Proxy relatedField -> [model] -> IO [Include relatedField model]
collectionFetchRelated relatedField model = do
relatedModels :: [relatedModel] <- query @relatedModel |> filterWhereIn (#id, map (getField @relatedField) model) |> fetch
Expand Down Expand Up @@ -120,6 +122,7 @@ instance (
, Show (PrimaryKey tableName)
, HasField "id" relatedModel (Id' tableName)
, relatedModel ~ GetModelByTableName (GetTableName relatedModel)
, Table relatedModel
) => CollectionFetchRelatedOrNothing (Id' tableName) relatedModel where
collectionFetchRelatedOrNothing :: forall model relatedField. (
?modelContext :: ModelContext,
Expand Down Expand Up @@ -158,7 +161,7 @@ instance (
-- This will query all posts with their comments. The type of @posts@ is @[Include "comments" Post]@.
--
-- When fetching query builders, currently the implementation is not very efficient. E.g. given 10 Posts above, it will run 10 queries to fetch the comments. We should optimise this behavior in the future.
instance (relatedModel ~ GetModelByTableName relatedTable) => CollectionFetchRelated (QueryBuilder relatedTable) relatedModel where
instance (relatedModel ~ GetModelByTableName relatedTable, Table relatedModel) => CollectionFetchRelated (QueryBuilder relatedTable) relatedModel where
collectionFetchRelated :: forall model relatedField. (
?modelContext :: ModelContext,
HasField relatedField model (QueryBuilder relatedTable),
Expand All @@ -181,7 +184,8 @@ fetchRelated :: forall model field fieldValue fetchModel. (
HasField field model fieldValue,
PG.FromRow fetchModel,
KnownSymbol (GetTableName fetchModel),
Fetchable fieldValue fetchModel
Fetchable fieldValue fetchModel,
Table fetchModel
) => Proxy field -> model -> IO (Include field model)
fetchRelated relatedField model = do
result :: FetchResult fieldValue fetchModel <- fetch ((getField @field model) :: fieldValue)
Expand All @@ -195,7 +199,8 @@ fetchRelatedOrNothing :: forall model field fieldValue fetchModel. (
HasField field model (Maybe fieldValue),
PG.FromRow fetchModel,
KnownSymbol (GetTableName fetchModel),
Fetchable fieldValue fetchModel
Fetchable fieldValue fetchModel,
Table fetchModel
) => Proxy field -> model -> IO (Include field model)
fetchRelatedOrNothing relatedField model = do
result :: Maybe (FetchResult fieldValue fetchModel) <- case getField @field model of
Expand All @@ -211,7 +216,8 @@ maybeFetchRelatedOrNothing :: forall model field fieldValue fetchModel. (
HasField field model (Maybe fieldValue),
PG.FromRow fetchModel,
KnownSymbol (GetTableName fetchModel),
Fetchable fieldValue fetchModel
Fetchable fieldValue fetchModel,
Table fetchModel
) => Proxy field -> Maybe model -> IO (Maybe (Include field model))
maybeFetchRelatedOrNothing relatedField = maybe (pure Nothing) (\q -> fetchRelatedOrNothing relatedField q >>= pure . Just)
{-# INLINE maybeFetchRelatedOrNothing #-}
2 changes: 1 addition & 1 deletion IHP/Job/Queue.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fetchNextJob :: forall job.
, FromRow job
, Show (PrimaryKey (GetTableName job))
, PG.FromField (PrimaryKey (GetTableName job))
, KnownSymbol (GetTableName job)
, Table job
) => UUID -> IO (Maybe job)
fetchNextJob workerId = do
let query = "UPDATE ? SET status = ?, locked_at = NOW(), locked_by = ?, attempts_count = attempts_count + 1 WHERE id IN (SELECT id FROM ? WHERE ((status = ?) OR (status = ? AND updated_at < NOW() + interval '30 seconds')) AND locked_by IS NULL AND run_at <= NOW() ORDER BY created_at LIMIT 1 FOR UPDATE) RETURNING id"
Expand Down
2 changes: 2 additions & 0 deletions IHP/Job/Runner.hs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ worker :: forall job.
, Job job
, CanUpdate job
, Show job
, Table job
) => JobWorker
worker = JobWorker (jobWorkerFetchAndRunLoop @job)

Expand All @@ -100,6 +101,7 @@ jobWorkerFetchAndRunLoop :: forall job.
, Job job
, CanUpdate job
, Show job
, Table job
) => JobWorkerArgs -> IO (Async.Async ())
jobWorkerFetchAndRunLoop JobWorkerArgs { .. } = do
let ?context = frameworkConfig
Expand Down
Loading

0 comments on commit 5fff5f6

Please sign in to comment.