| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
IHP.ModelSupport
Contents
Synopsis
- class KnownSymbol (GetTableName record) => Table record where
- tableName :: Text
- columnNames :: [Text]
- primaryKeyColumnNames :: [Text]
- isValid :: HasField "meta" record MetaBag => record -> Bool
- ids :: HasField "id" record id => [record] -> [id]
- commitTransaction :: (?modelContext :: ModelContext) => IO ()
- class Record model where
- newRecord :: model
- withTransaction :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext) => IO a) -> IO a
- didTouchField :: forall (fieldName :: Symbol) fieldValue record. (KnownSymbol fieldName, HasField fieldName record fieldValue, HasField "meta" record MetaBag, Eq fieldValue, Typeable record) => Proxy fieldName -> record -> Bool
- getModelName :: KnownSymbol (GetModelName model) => Text
- isNew :: HasField "meta" model MetaBag => model -> Bool
- notConnectedModelContext :: Logger -> ModelContext
- withModelContext :: ByteString -> Logger -> (ModelContext -> IO a) -> IO a
- createRecord :: (?modelContext :: ModelContext, CanCreate model) => model -> IO model
- deleteRecord :: forall record (table :: Symbol). (?modelContext :: ModelContext, Table record, Show (PrimaryKey table), HasField "id" record (Id record), GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder (Id' table)) => record -> IO ()
- createModelContext :: ByteString -> Logger -> IO ModelContext
- sqlQuery :: (?modelContext :: ModelContext, ToSnippetParams q, FromRowHasql r) => Query -> q -> IO [r]
- sqlQueryScalar :: (?modelContext :: ModelContext, ToSnippetParams q, HasqlDecodeColumn value) => Query -> q -> IO value
- didChange :: forall (fieldName :: Symbol) fieldValue record. (KnownSymbol fieldName, HasField fieldName record fieldValue, HasField "meta" record MetaBag, Eq fieldValue, Typeable record) => Proxy fieldName -> record -> Bool
- releaseModelContext :: ModelContext -> IO ()
- unpackId :: forall (model :: Symbol). Id' model -> PrimaryKey model
- recordToInputValue :: (HasField "id" entity (Id entity), Show (PrimaryKey (GetTableName entity))) => entity -> Text
- packId :: forall (model :: Symbol). PrimaryKey model -> Id' model
- textToId :: forall (model :: Symbol) text. (HasCallStack, ParsePrimaryKey (PrimaryKey model), ConvertibleStrings text Text) => text -> Id' model
- sqlQueryHasql :: (?modelContext :: ModelContext) => Pool -> Snippet -> Result a -> IO a
- sqlQuerySingleRow :: (?modelContext :: ModelContext, ToSnippetParams query, FromRowHasql record) => Query -> query -> IO record
- sqlExec :: (?modelContext :: ModelContext, ToSnippetParams q) => Query -> q -> IO Int64
- sqlExecHasqlCount :: (?modelContext :: ModelContext) => Pool -> Snippet -> IO Int64
- sqlExecDiscardResult :: (?modelContext :: ModelContext, ToSnippetParams q) => Query -> q -> IO ()
- setRLSConfigStatement :: Statement (Text, Text) ()
- isCachedPlanError :: UsageError -> Bool
- data HasqlError = HasqlError UsageError
- truncateQuery :: Text -> Text
- sqlExecHasql :: (?modelContext :: ModelContext) => Pool -> Snippet -> IO ()
- runSessionHasql :: (?modelContext :: ModelContext) => Pool -> Session () -> IO ()
- data SessionRequest where
- SessionRequest :: forall a. Session a -> MVar (Either SessionError a) -> SessionRequest
- processRequests :: MVar (Maybe SessionRequest) -> Session ()
- isCachedPlanSessionError :: SessionError -> Bool
- sqlQueryScalarOrNothing :: (?modelContext :: ModelContext, ToSnippetParams q, HasqlDecodeColumn value) => Query -> q -> IO (Maybe value)
- withRowLevelSecurityDisabled :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext) => IO a) -> IO a
- rollbackTransaction :: (?modelContext :: ModelContext) => IO ()
- primaryKeyConditionColumnSelector :: Table record => Text
- deleteRecordById :: forall record (table :: Symbol). (?modelContext :: ModelContext, Table record, Show (PrimaryKey table), GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder (Id' table)) => Id' table -> IO ()
- deleteRecords :: forall record (table :: Symbol). (?modelContext :: ModelContext, Show (PrimaryKey table), Table record, HasField "id" record (Id' table), GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder [Id' table]) => [record] -> IO ()
- deleteRecordByIds :: forall record (table :: Symbol). (?modelContext :: ModelContext, Show (PrimaryKey table), Table record, GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder [Id' table]) => [Id' table] -> IO ()
- deleteAll :: (?modelContext :: ModelContext, Table record) => IO ()
- didChangeRecord :: HasField "meta" record MetaBag => record -> Bool
- fieldWithDefault :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag) => Proxy name -> model -> FieldWithDefault value
- fieldWithUpdate :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag) => Proxy name -> model -> FieldWithUpdate name value
- fieldWithDefaultSnippet :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag, DefaultParamEncoder value) => Proxy name -> model -> Snippet
- fieldWithUpdateSnippet :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag, DefaultParamEncoder value) => Proxy name -> model -> Snippet
- trackTableRead :: (?modelContext :: ModelContext) => Text -> IO ()
- withTableReadTracker :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext, ?touchedTables :: IORef (Set Text)) => IO ()) -> IO ()
- onlyWhere :: forall record (fieldName :: Symbol) value. (KnownSymbol fieldName, HasField fieldName record value, Eq value) => Proxy fieldName -> value -> [record] -> [record]
- onlyWhereReferences :: forall record (fieldName :: Symbol) value referencedRecord. (KnownSymbol fieldName, HasField fieldName record value, Eq value, HasField "id" referencedRecord value) => Proxy fieldName -> referencedRecord -> [record] -> [record]
- onlyWhereReferencesMaybe :: forall record (fieldName :: Symbol) value referencedRecord. (KnownSymbol fieldName, HasField fieldName record (Maybe value), Eq value, HasField "id" referencedRecord value) => Proxy fieldName -> referencedRecord -> [record] -> [record]
- copyRecord :: (Table record, SetField "id" record id, Default id, SetField "meta" record MetaBag) => record -> record
- withoutQueryLogging :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext) => result) -> result
- module IHP.ModelSupport.Types
- data Point = Point Double Double
- fromCoordinates :: Double -> Double -> Point
- toX :: Point -> Double
- toY :: Point -> Double
- data Polygon
- refineFromPointList :: [(Double, Double)] -> Maybe Polygon
- refineFromPointVector :: Vector (Double, Double) -> Maybe Polygon
- toPointList :: Polygon -> [(Double, Double)]
- toPointVector :: Polygon -> Vector (Double, Double)
- fold :: (Word32 -> Word8 -> a) -> (Word32 -> Word32 -> Word32 -> Word32 -> Word8 -> a) -> Inet -> a
- data Inet
- normalizeFromV4 :: Word32 -> Word8 -> Inet
- normalizeFromV6 :: Word32 -> Word32 -> Word32 -> Word32 -> Word8 -> Inet
- refineFromV4 :: Word32 -> Word8 -> Maybe Inet
- refineFromV6 :: Word32 -> Word32 -> Word32 -> Word32 -> Word8 -> Maybe Inet
- refineToV4 :: Inet -> Maybe (Word32, Word8)
- refineToV6 :: Inet -> Maybe (Word32, Word32, Word32, Word32, Word8)
- data Weight
- data Tsvector
- normalizeFromLexemeList :: [(Text, [(Word16, Weight)])] -> Tsvector
- refineFromLexemeList :: [(Text, [(Word16, Weight)])] -> Maybe Tsvector
- toLexemeList :: Tsvector -> [(Text, [(Word16, Weight)])]
- toMicroseconds :: Interval -> Int64
- data Interval
- normalizeFromDiffTime :: DiffTime -> Interval
- normalizeFromMicrosecondsInTotal :: Integer -> Interval
- normalizeFromMonthsDaysAndMicroseconds :: Int32 -> Int32 -> Int64 -> Interval
- normalizeToDiffTime :: Interval -> DiffTime
- normalizeToMicrosecondsInTotal :: Interval -> Integer
- refineFromDiffTime :: DiffTime -> Maybe Interval
- refineFromMicrosecondsInTotal :: Integer -> Maybe Interval
- refineFromMonthsDaysAndMicroseconds :: Int32 -> Int32 -> Int64 -> Maybe Interval
- toDays :: Interval -> Int32
- toMonths :: Interval -> Int32
- module IHP.InputValue
Documentation
class KnownSymbol (GetTableName record) => Table record where Source #
Access meta data for a database table
Minimal complete definition
Methods
Returns the table name of a given model.
Example:
>>>tableName @User"users"
columnNames :: [Text] Source #
Returns the list of column names for a given model
Example:
>>>columnNames @User["id", "email", "created_at"]
primaryKeyColumnNames :: [Text] Source #
Returns the list of column names, that are contained in the primary key for a given model
Example:
>>>primaryKeyColumnNames @User["id"]
>>>primaryKeyColumnNames @PostTagging["post_id", "tag_id"]
isValid :: HasField "meta" record MetaBag => record -> Bool Source #
Returns True when a record has no validation errors attached from a previous validation call
Example:
isValidProject :: Project -> Bool
isValidProject project =
project
|> validateField #name isNonEmpty
|> isValidids :: HasField "id" record id => [record] -> [id] Source #
Returns the ids for a list of models
Shorthand for map (.id) records.
>>>users <- query @User |> fetch>>>ids users[227fbba3-0578-4eb8-807d-b9b692c3644f, 9d7874f2-5343-429b-bcc4-8ee62a5a6895, ...] :: [Id User]
commitTransaction :: (?modelContext :: ModelContext) => IO () Source #
withTransaction :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext) => IO a) -> IO a Source #
Executes the given block with a database transaction
Example:
withTransaction do
company <- newRecord @Company |> createRecord
-- When creating the user fails, there will be no company left over
user <- newRecord @User
|> set #companyId company.id
|> createRecord
company <- company
|> set #ownerId user.id
|> updateRecorddidTouchField :: forall (fieldName :: Symbol) fieldValue record. (KnownSymbol fieldName, HasField fieldName record fieldValue, HasField "meta" record MetaBag, Eq fieldValue, Typeable record) => Proxy fieldName -> record -> Bool Source #
Returns True if set was called on that field
Example: Returns False for freshly fetched records
>>>let projectId = "227fbba3-0578-4eb8-807d-b9b692c3644f" :: Id Project>>>project <- fetch projectId>>>didTouchField #name projectFalse
Example: Returns True after setting a field
>>>let projectId = "227fbba3-0578-4eb8-807d-b9b692c3644f" :: Id Project>>>project <- fetch projectId>>>project |> set #name project.name |> didTouchField #nameTrue
getModelName :: KnownSymbol (GetModelName model) => Text Source #
Returns the model name of a given model as Text
Example:
>>>modelName @User"User"
>>>modelName @Project"Project"
isNew :: HasField "meta" model MetaBag => model -> Bool Source #
Returns True when the record has not been saved to the database yet. Returns False otherwise.
Example: Returns True when a record has not been inserted yet.
>>>let project = newRecord @Project>>>isNew projectTrue
Example: Returns False after inserting a record.
>>>project <- createRecord project>>>isNew projectFalse
Example: Returns False for records which have been fetched from the database.
>>>book <- query @Book |> fetchOne>>>isNew bookFalse
notConnectedModelContext :: Logger -> ModelContext Source #
Provides a mock ModelContext to be used when a database connection is not available
withModelContext :: ByteString -> Logger -> (ModelContext -> IO a) -> IO a Source #
Bracket-style wrapper around createModelContext that ensures the database
pool is released when the callback completes (or throws an exception).
createRecord :: (?modelContext :: ModelContext, CanCreate model) => model -> IO model Source #
deleteRecord :: forall record (table :: Symbol). (?modelContext :: ModelContext, Table record, Show (PrimaryKey table), HasField "id" record (Id record), GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder (Id' table)) => record -> IO () Source #
Runs a DELETE query for a record.
>>>let project :: Project = ...>>>deleteRecord projectDELETE FROM projects WHERE id = '..'
Use deleteRecords if you want to delete multiple records.
createModelContext :: ByteString -> Logger -> IO ModelContext Source #
sqlQuery :: (?modelContext :: ModelContext, ToSnippetParams q, FromRowHasql r) => Query -> q -> IO [r] Source #
Runs a raw sql query
Example:
users <- sqlQuery "SELECT id, firstname, lastname FROM users" ()
Take a look at IHP.QueryBuilder for a typesafe approach on building simple queries.
- AutoRefresh:* When using
sqlQuerywith AutoRefresh, you need to usetrackTableReadto let AutoRefresh know that you have accessed a certain table. Otherwise AutoRefresh will not watch table of your custom sql query.
Use sqlQuerySingleRow if you expect only a single row to be returned.
sqlQueryScalar :: (?modelContext :: ModelContext, ToSnippetParams q, HasqlDecodeColumn value) => Query -> q -> IO value Source #
Runs a raw sql query which results in a single scalar value such as an integer or string
Example:
usersCount <- sqlQueryScalar "SELECT COUNT(*) FROM users"
Take a look at IHP.QueryBuilder for a typesafe approach on building simple queries.
didChange :: forall (fieldName :: Symbol) fieldValue record. (KnownSymbol fieldName, HasField fieldName record fieldValue, HasField "meta" record MetaBag, Eq fieldValue, Typeable record) => Proxy fieldName -> record -> Bool Source #
Returns True if the specific field of the record has unsaved changes
Example: Returns False for freshly fetched records
>>>let projectId = "227fbba3-0578-4eb8-807d-b9b692c3644f" :: Id Project>>>project <- fetch projectId>>>didChange #name projectFalse
Example: Returns True after setting a field
>>>let projectId = "227fbba3-0578-4eb8-807d-b9b692c3644f" :: Id Project>>>project <- fetch projectId>>>project |> set #name "New Name" |> didChange #nameTrue
Example: Setting a flash message after updating the profile picture
when (user |> didChange #profilePictureUrl) (setSuccessMessage "Your Profile Picture has been updated. It might take a few minutes until it shows up everywhere")
releaseModelContext :: ModelContext -> IO () Source #
unpackId :: forall (model :: Symbol). Id' model -> PrimaryKey model Source #
Unwraps a Id value into an UUID
>>>unpackId ("296e5a50-b237-4ee9-83b0-17fb1e6f208f" :: Id User)"296e5a50-b237-4ee9-83b0-17fb1e6f208f" :: UUID
recordToInputValue :: (HasField "id" entity (Id entity), Show (PrimaryKey (GetTableName entity))) => entity -> Text Source #
packId :: forall (model :: Symbol). PrimaryKey model -> Id' model Source #
Turns an UUID into a Id type
let uuid :: UUID = "5240e79c-97ff-4a5f-8567-84112541aaba" let userId :: Id User = packId uuid
textToId :: forall (model :: Symbol) text. (HasCallStack, ParsePrimaryKey (PrimaryKey model), ConvertibleStrings text Text) => text -> Id' model Source #
Transforms a text, bytestring or string into an Id. Throws an exception if the input is invalid.
Example:
let projectIdText = "7cbc76e2-1c4f-49b6-a7d9-5015e7575a9b" :: Text let projectId = (textToId projectIdText) :: Id Project
In case your UUID value is hardcoded, there is also an IsString instance, so you
can just write it like:
let projectId = "ca63aace-af4b-4e6c-bcfa-76ca061dbdc6" :: Id Project
sqlQueryHasql :: (?modelContext :: ModelContext) => Pool -> Snippet -> Result a -> IO a Source #
Runs a query using the hasql pool with prepared statements
This function executes a query using hasql's prepared statement mechanism, which provides better performance than postgresql-simple for repeated queries.
When RLS is enabled, the query is wrapped in a transaction that first sets the
role and user id via setRLSConfigStatement.
Example:
users <- sqlQueryHasql pool snippet (Decoders.rowList userDecoder)
sqlQuerySingleRow :: (?modelContext :: ModelContext, ToSnippetParams query, FromRowHasql record) => Query -> query -> IO record Source #
Runs a raw sql query, that is expected to return a single result row
Like sqlQuery, but useful when you expect only a single row as the result
Example:
user <- sqlQuerySingleRow "SELECT id, firstname, lastname FROM users WHERE id = ?" (Only user.id)
Take a look at IHP.QueryBuilder for a typesafe approach on building simple queries.
- AutoRefresh:* When using
sqlQuerySingleRowwith AutoRefresh, you need to usetrackTableReadto let AutoRefresh know that you have accessed a certain table. Otherwise AutoRefresh will not watch table of your custom sql query.
sqlExec :: (?modelContext :: ModelContext, ToSnippetParams q) => Query -> q -> IO Int64 Source #
Runs a sql statement (like a CREATE statement)
Example:
sqlExec "CREATE TABLE users ()" ()
sqlExecHasqlCount :: (?modelContext :: ModelContext) => Pool -> Snippet -> IO Int64 Source #
Like sqlExecHasql but returns the number of affected rows
When RLS is enabled, the statement is wrapped in a transaction that first sets the
role and user id via setRLSConfigStatement.
sqlExecDiscardResult :: (?modelContext :: ModelContext, ToSnippetParams q) => Query -> q -> IO () Source #
Runs a sql statement (like a CREATE statement), but doesn't return any result
Example:
sqlExecDiscardResult "CREATE TABLE users ()" ()
setRLSConfigStatement :: Statement (Text, Text) () Source #
Prepared statement that sets the RLS role and user id using set_config().
Uses set_config(setting, value, is_local) which is a regular SQL function
that supports parameterized values in the extended query protocol, unlike
SET LOCAL which is a utility command that cannot be parameterized.
The third argument true makes the setting local to the current transaction,
equivalent to SET LOCAL.
isCachedPlanError :: UsageError -> Bool Source #
Detects errors caused by stale schema after make db recreates the database.
Matches four categories:
- PostgreSQL "cached plan must not change result type" (error code 0A000) — the server rejects a prepared statement whose result columns changed.
- PostgreSQL "cache lookup failed for type" (error code XX000) — a prepared statement references a type OID that no longer exists after schema recreation (types get new OIDs).
- Hasql
MissingTypesSessionError— custom enum types (e.g.JOB_STATUS) get new OIDs after schema recreation, and hasql's type registry can't find them. - Hasql
UnexpectedColumnTypeStatementError— the column's type OID no longer matches the OID cached in the prepared statement / decoder.
data HasqlError Source #
Exception type for hasql errors
Constructors
| HasqlError UsageError |
Instances
| Exception HasqlError Source # | |
Defined in IHP.ModelSupport Methods toException :: HasqlError -> SomeException # fromException :: SomeException -> Maybe HasqlError # displayException :: HasqlError -> String # backtraceDesired :: HasqlError -> Bool # | |
| Show HasqlError Source # | |
Defined in IHP.ModelSupport Methods showsPrec :: Int -> HasqlError -> ShowS # show :: HasqlError -> String # showList :: [HasqlError] -> ShowS # | |
truncateQuery :: Text -> Text Source #
sqlExecHasql :: (?modelContext :: ModelContext) => Pool -> Snippet -> IO () Source #
Like sqlQueryHasql but for statements that don't return results (DELETE, etc.)
When RLS is enabled, the statement is wrapped in a transaction that first sets the
role and user id via setRLSConfigStatement.
runSessionHasql :: (?modelContext :: ModelContext) => Pool -> Session () -> IO () Source #
Like sqlExecHasql but for raw Session values (e.g. multi-statement DDL via sql)
Use this instead of sqlExecHasql when you need the simple protocol (no prepared statements),
e.g. for multi-statement SQL like trigger creation.
Example:
runSessionHasql pool (Hasql.sql "BEGIN; CREATE ...; COMMIT;")
data SessionRequest where Source #
Existential wrapper for sub-session requests in a transaction
Constructors
| SessionRequest :: forall a. Session a -> MVar (Either SessionError a) -> SessionRequest |
processRequests :: MVar (Maybe SessionRequest) -> Session () Source #
Loop that reads sub-session requests from an MVar and executes them
on the current transaction's connection. Stops when it receives Nothing.
sqlQueryScalarOrNothing :: (?modelContext :: ModelContext, ToSnippetParams q, HasqlDecodeColumn value) => Query -> q -> IO (Maybe value) Source #
Runs a raw sql query which results in a single scalar value such as an integer or string, or nothing
Example:
usersCount <- sqlQueryScalarOrNothing "SELECT COUNT(*) FROM users"
Take a look at IHP.QueryBuilder for a typesafe approach on building simple queries.
withRowLevelSecurityDisabled :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext) => IO a) -> IO a Source #
Executes the given block with the main database role and temporarly sidesteps the row level security policies.
This is used e.g. by IHP AutoRefresh to be able to set up it's database triggers. When trying to set up a database
trigger from the ihp_authenticated role, it typically fails because it's missing permissions. Using withRowLevelSecurityDisabled
we switch to the main role which is allowed to set up database triggers.
SQL queries run from within the passed block are executed in their own transaction.
Example:
-- SQL code executed here might be run from the ihp_authenticated role withRowLevelSecurityDisabled do -- SQL code executed here is run as the main IHP db role sqlExec "CREATE OR REPLACE FUNCTION .." ()
rollbackTransaction :: (?modelContext :: ModelContext) => IO () Source #
primaryKeyConditionColumnSelector :: Table record => Text Source #
Returns ByteString, that represents the part of an SQL where clause, that matches on a tuple consisting of all the primary keys
For table with simple primary keys this simply returns the name of the primary key column, without wrapping in a tuple
>>> primaryKeyColumnSelector PostTag
"(post_tags.post_id, post_tags.tag_id)"
>>> primaryKeyColumnSelector Post
"post_tags.post_id"
deleteRecordById :: forall record (table :: Symbol). (?modelContext :: ModelContext, Table record, Show (PrimaryKey table), GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder (Id' table)) => Id' table -> IO () Source #
Like deleteRecord but using an Id
>>>let project :: Id Project = ...>>>delete projectIdDELETE FROM projects WHERE id = '..'
deleteRecords :: forall record (table :: Symbol). (?modelContext :: ModelContext, Show (PrimaryKey table), Table record, HasField "id" record (Id' table), GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder [Id' table]) => [record] -> IO () Source #
Runs a DELETE query for a list of records.
>>>let projects :: [Project] = ...>>>deleteRecords projectsDELETE FROM projects WHERE id IN (..)
deleteRecordByIds :: forall record (table :: Symbol). (?modelContext :: ModelContext, Show (PrimaryKey table), Table record, GetTableName record ~ table, record ~ GetModelByTableName table, DefaultParamEncoder [Id' table]) => [Id' table] -> IO () Source #
Like deleteRecordById but for a list of Ids.
>>>let projectIds :: [ Id Project ] = ...>>>delete projectIdsDELETE FROM projects WHERE id IN ('..')
deleteAll :: (?modelContext :: ModelContext, Table record) => IO () Source #
Runs a DELETE query to delete all rows in a table.
>>>deleteAll @ProjectDELETE FROM projects
didChangeRecord :: HasField "meta" record MetaBag => record -> Bool Source #
Returns True if any fields of the record have unsaved changes
Example: Returns False for freshly fetched records
>>>let projectId = "227fbba3-0578-4eb8-807d-b9b692c3644f" :: Id Project>>>project <- fetch projectId>>>didChangeRecord projectFalse
Example: Returns True after setting a field
>>>let projectId = "227fbba3-0578-4eb8-807d-b9b692c3644f" :: Id Project>>>project <- fetch projectId>>>project |> set #name "New Name" |> didChangeRecordTrue
fieldWithDefault :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag) => Proxy name -> model -> FieldWithDefault value Source #
Construct a FieldWithDefault
Use the default SQL value when the field hasn't been touched since the
record was created. This information is stored in the touchedFields
attribute of the meta field.
fieldWithUpdate :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag) => Proxy name -> model -> FieldWithUpdate name value Source #
Construct a FieldWithUpdate
Use the current database value when the field hasn't been touched since the
record was accessed. This information is stored in the touchedFields
attribute of the meta field.
fieldWithDefaultSnippet :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag, DefaultParamEncoder value) => Proxy name -> model -> Snippet Source #
Like fieldWithDefault but produces a hasql Snippet instead of a FieldWithDefault
When the field hasn't been touched, produces DEFAULT. Otherwise encodes the value
using hasql's DefaultParamEncoder.
fieldWithUpdateSnippet :: forall (name :: Symbol) model value. (KnownSymbol name, HasField name model value, HasField "meta" model MetaBag, DefaultParamEncoder value) => Proxy name -> model -> Snippet Source #
Like fieldWithUpdate but produces a hasql Snippet instead of a FieldWithUpdate
When the field hasn't been touched, produces the column name (keeping the current DB value).
Otherwise encodes the new value using hasql's DefaultParamEncoder.
trackTableRead :: (?modelContext :: ModelContext) => Text -> IO () Source #
Useful to manually mark a table read when doing a custom sql query inside AutoRefresh or withTableReadTracker.
When using fetch on a query builder, this function is automatically called. That's why you only need to call
it yourself when using sqlQuery to run a custom query.
Example:
action MyAction = autoRefresh do
users <- sqlQuery "SELECT * FROM users WHERE .."
trackTableRead "users"
render MyView { .. }withTableReadTracker :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext, ?touchedTables :: IORef (Set Text)) => IO ()) -> IO () Source #
Track all tables in SELECT queries executed within the given IO action.
You can read the touched tables by this function by accessing the variable ?touchedTables inside your given IO action.
Example:
withTableReadTracker do
project <- query @Project |> fetchOne
user <- query @User |> fetchOne
tables <- readIORef ?touchedTables
-- tables = Set.fromList ["projects", "users"]
onlyWhere :: forall record (fieldName :: Symbol) value. (KnownSymbol fieldName, HasField fieldName record value, Eq value) => Proxy fieldName -> value -> [record] -> [record] Source #
Shorthand filter function
In IHP code bases you often write filter functions such as these:
getUserPosts user posts =
filter (\p -> p.userId == user.id) postsThis can be written in a shorter way using onlyWhere:
getUserPosts user posts =
posts |> onlyWhere #userId user.idBecause the userId field is an Id, we can use onlyWhereReferences to make it even shorter:
getUserPosts user posts =
posts |> onlyWhereReferences #userId userIf the Id field is nullable, we need to use onlyWhereReferencesMaybe:
getUserTasks user tasks =
tasks |> onlyWhereReferencesMaybe #optionalUserId useronlyWhereReferences :: forall record (fieldName :: Symbol) value referencedRecord. (KnownSymbol fieldName, HasField fieldName record value, Eq value, HasField "id" referencedRecord value) => Proxy fieldName -> referencedRecord -> [record] -> [record] Source #
Shorthand filter function for Id fields
In IHP code bases you often write filter functions such as these:
getUserPosts user posts =
filter (\p -> p.userId == user.id) postsThis can be written in a shorter way using onlyWhereReferences:
getUserPosts user posts =
posts |> onlyWhereReferences #userId userIf the Id field is nullable, we need to use onlyWhereReferencesMaybe:
getUserTasks user tasks =
tasks |> onlyWhereReferencesMaybe #optionalUserId userSee onlyWhere for more details.
onlyWhereReferencesMaybe :: forall record (fieldName :: Symbol) value referencedRecord. (KnownSymbol fieldName, HasField fieldName record (Maybe value), Eq value, HasField "id" referencedRecord value) => Proxy fieldName -> referencedRecord -> [record] -> [record] Source #
Shorthand filter function for nullable Id fields
In IHP code bases you often write filter functions such as these:
getUserTasks user tasks =
filter (\task -> task.optionalUserId == Just user.id) tasksThis can be written in a shorter way using onlyWhereReferencesMaybe:
getUserTasks user tasks =
tasks |> onlyWhereReferencesMaybe #optionalUserId userSee onlyWhere for more details.
copyRecord :: (Table record, SetField "id" record id, Default id, SetField "meta" record MetaBag) => record -> record Source #
Copies all the fields (except the id field) into a new record
Example: Duplicate a database record (except the primary key of course)
project <- fetch projectId duplicatedProject <- createRecord (copyRecord project)
withoutQueryLogging :: (?modelContext :: ModelContext) => ((?modelContext :: ModelContext) => result) -> result Source #
Runs sql queries without logging them
Example:
users <- withoutQueryLogging (sqlQuery "SELECT * FROM users" ())
module IHP.ModelSupport.Types
Instances
fromCoordinates :: Double -> Double -> Point #
Instances
toPointList :: Polygon -> [(Double, Double)] #
fold :: (Word32 -> Word8 -> a) -> (Word32 -> Word32 -> Word32 -> Word32 -> Word8 -> a) -> Inet -> a #
Instances
normalizeFromV4 :: Word32 -> Word8 -> Inet #
Instances
toMicroseconds :: Interval -> Int64 #
Instances
normalizeToDiffTime :: Interval -> DiffTime #
refineFromDiffTime :: DiffTime -> Maybe Interval #
module IHP.InputValue