-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An ORM (Object Relational Mapping) and migrations DSL for PostgreSQL. -- -- An ORM (Object Relational Mapping) and migrations DSL for PostgreSQL. -- See Database.PostgreSQL.ORM for documentation. @package postgresql-orm @version 0.1 module Database.PostgreSQL.ORM.Validations data InvalidError InvalidError :: !ByteString -> !ByteString -> InvalidError invalidColumn :: InvalidError -> !ByteString invalidError :: InvalidError -> !ByteString newtype ValidationError ValidationError :: [InvalidError] -> ValidationError type ValidationFunc a = a -> [InvalidError] validate :: (a -> Bool) -> ByteString -> ByteString -> ValidationFunc a validateNotEmpty :: (a -> Text) -> ByteString -> ByteString -> ValidationFunc a instance Typeable ValidationError instance Show InvalidError instance Show ValidationError instance Exception ValidationError -- | This module deals with escaping and sanitizing SQL templates. module Database.PostgreSQL.Escape -- | Take a SQL template containing '?' characters and a list of paremeters -- whose length must match the number of '?' characters, and format the -- result as an escaped ByteString that can be used as a query. -- -- Like formatQuery, this function is naive about the placement of -- '?' characters and will expand all of them, even ones within quotes. -- To avoid this, you must use quoteIdent on identifiers -- containing question marks. -- -- Also like formatQuery, '?' characters touching other '?' -- characters or quoted strings may do the wrong thing, and end up -- doubling a quote, so avoid substrings such as "??" or -- "?string", as these could get expanded to, e.g., -- "'param''string'", which is a single string containing an -- apostrophe, when you probably wanted two strings. fmtSql :: ToRow p => Query -> p -> Query -- | Quote an identifier such as a table or column name using double-quote -- characters. Note this has nothing to do with quoting values, -- which must be quoted using single quotes. (Anyway, all values should -- be quoted by query or fmtSql.) This function uses a -- unicode escape sequence to escape '?' characters, which would -- otherwise be expanded by query, formatQuery, or -- fmtSql. -- --
-- >>> S8.putStrLn $ quoteIdent "hello \"world\"!" -- "hello ""world""!" -- -- >>> S8.putStrLn $ quoteIdent "hello \"world\"?" -- U&"hello ""world""\003f" ---- -- Note that this quoting function is correct only if -- client_encoding is SQL_ASCII, client_coding -- is UTF8, or the identifier contains no multi-byte characters. -- For other coding schemes, this function may erroneously duplicate -- bytes that look like quote characters but are actually part of a -- multi-byte character code. In such cases, maliciously crafted -- identifiers will, even after quoting, allow injection of arbitrary SQL -- commands to the server. -- -- The upshot is that it is unwise to use this function on identifiers -- provided by untrustworthy sources. Note this is true anyway, -- regardless of client_encoding setting, because certain -- "system column" names (e.g., oid, tableoid, -- xmin, cmin, xmax, cmax, -- ctid) are likely to produce unexpected results even when -- properly quoted. -- -- See Id for a convenient way to include quoted identifiers in -- parameter lists. quoteIdent :: ByteString -> ByteString -- | An identifier is a table or column name. When rendered into a SQL -- query by fmtSql, it will be double-quoted, rather than -- single-quoted. For example: -- --
-- >>> fmtSql "select * from ? where name = ?" (Id "MyTable", "A Name") -- "select * from \"MyTable\" where name = E'A Name'" --newtype Id Id :: ByteString -> Id -- | A builder version of fmtSql, possibly useful if you are about -- to concatenate various individually formatted query fragments and want -- to save the work of concatenating each individually. buildSql :: ToRow p => Query -> p -> Builder -- | A lower-level function used by buildSql and fmtSql. You -- probably don't need to call it directly. buildSqlFromActions :: Query -> [Action] -> Builder buildAction :: Action -> Builder buildLiteral :: ByteString -> Builder buildByteA :: ByteString -> Builder -- | Build a quoted identifier. Generally you will want to use -- quoteIdent, and for repeated use it will be faster to use -- fromByteString . quoteIdent, but this internal -- function is exposed in case it is useful. buildIdent :: ByteString -> Builder instance Show Id instance ToField Id instance IsString Id -- | Functions to help with building database migrations. -- -- Most users will want to create a database migration using -- defaultMain as follows, -- --
-- import Database.PostgreSQL.Migrations -- -- main = defaultMain up down -- -- up = migrate $ do -- create_table "posts" -- [ column "title" "VARCHAR(255) NOT NULL" -- , column "author_id" "integer references authors(id)"] -- -- down = migrate $ drop_table "posts" --module Database.PostgreSQL.Migrations defaultMain :: (Connection -> IO ()) -> (Connection -> IO ()) -> IO () -- | Creates a PostgreSQL Connection using the DATABASE_URL -- environment variable, if it exists. If it does, it should match the -- format: -- --
-- postgresql://[[USERNAME@PASSWORD]HOSTNAME[:PORT]]/[DBNAME] ---- -- If it is not present, the environment variables PG_DBNAME -- PG_HOST etc, are used. connectEnv :: IO Connection -- | Runs the SQL file at the given path, relative to the current working -- directory. runSqlFile :: FilePath -> Migration () type Migration = ReaderT Connection IO migrate :: Migration a -> Connection -> IO () -- | Returns a column defition by quoting the given name column :: ByteString -> ByteString -> ByteString -- | Creates a table. See column for constructing the column list. create_table :: ByteString -> [ByteString] -> Migration Int64 -- | Adds a column to the given table. For example, -- --
-- add_column "posts" "title" "VARCHAR(255)" ---- -- adds a varchar column called "title" to the table "posts". add_column :: ByteString -> ByteString -> ByteString -> Migration Int64 -- | Creates an index for efficient lookup. create_index :: ByteString -> ByteString -> [ByteString] -> Migration Int64 -- | Creates a unique index for efficient lookup. create_unique_index :: ByteString -> ByteString -> [ByteString] -> Migration Int64 -- | Drops a table drop_table :: ByteString -> Migration Int64 -- | Drops a column from the given table. For example, -- --
-- drop_column "posts" "title" ---- -- drops the column "title" from the "posts" table. drop_column :: ByteString -> ByteString -> Migration Int64 -- | Drops an index. drop_index :: ByteString -> Migration Int64 -- | Renames a column in the given table. For example, -- --
-- rename_column "posts" "title" "name" ---- -- renames the column "title" in the "posts" table to "name". rename_column :: ByteString -> ByteString -> ByteString -> Migration Int64 -- | Alters a column in the given table. For example, -- --
-- change_column "posts" "title" "DROP DEFAULT" ---- -- drops the default constraint for the "title" column in the "posts" -- table. change_column :: ByteString -> ByteString -> ByteString -> Migration Int64 -- | Returns a Query that creates a table, for example: -- --
-- create_table "posts" -- [ column "title" "VARCHAR(255) NOT NULL" -- , column "body" "text"] --create_table_stmt :: ByteString -> [ByteString] -> Query -- | Returns a Query that adds a column to the given table. For -- example, -- --
-- add_column "posts" "title" "VARCHAR(255)" ---- -- Returns the query -- --
-- ALTER TABLE "posts" add "title" VARCHAR(255); --add_column_stmt :: ByteString -> ByteString -> ByteString -> Query -- | Returns a Query that creates an index for the given columns on -- the given table. For example, -- --
-- create_index_stmt "post_owner_index" "posts" "owner" ---- -- Returns the query -- --
-- CREATE INDEX "post_owner_index" ON "posts" ("owner")
--
create_index_stmt :: Bool -> ByteString -> ByteString -> [ByteString] -> Query
-- | Returns a Query that drops a table
drop_table_stmt :: ByteString -> Query
-- | Returns a Query that drops a column from the given table. For
-- example,
--
-- -- drop_column "posts" "title" ---- -- Returns the query -- --
-- ALTER TABLE "posts" add "title"; --drop_column_stmt :: ByteString -> ByteString -> Query -- | Returns a Query that drops an index. -- --
-- drop_index_stmt "post_owner_index" ---- -- Returns the query -- --
-- DROP INDEX "post_owner_index" --drop_index_stmt :: ByteString -> Query -- | Returns a Query that renames a column in the given table. For -- example, -- --
-- rename_column "posts" "title" "name" ---- -- Returns the query -- --
-- ALTER TABLE "posts" RENAME "title" TO "name"; --rename_column_stmt :: ByteString -> ByteString -> ByteString -> Query -- | Returns a Query that alters a column in the given table. For -- example, -- --
-- change_column "posts" "title" "DROP DEFAULT" ---- -- Returns the query -- --
-- ALTER TABLE "posts" ALTER "title" DROP DEFAULT; --change_column_stmt :: ByteString -> ByteString -> ByteString -> Query -- | Functions for creating and running database migrations. You should -- probably be using the pg_migrate executable to run -- migrations, however these functions are exposed for developers that -- want to integrate migrations more tightly into their applications or -- utilities. module Database.PostgreSQL.Migrate -- | Initializes the database by creating a "schema-migrations" table. This -- table must exist before running any migrations. initializeDb :: IO () -- | Runs all new migrations in a given directory and dumps the resulting -- schema to a file "schema.sql" in the migrations directory. -- -- Determining which migrations to run is done by querying the database -- for the largest version in the schema_migrations table, and -- choosing all migrations in the given directory with higher versions. runMigrationsForDir :: Handle -> FilePath -> IO ExitCode runRollbackForDir :: FilePath -> IO ExitCode -- | Dumps the database schema to the given file handle. -- -- This is a wrapper around the utility pg_dump that comes with -- postgresql. Therefore, pg_dump must be installed on the system. dumpDb :: Handle -> IO ExitCode -- | The default relative path containing migrations: "./dir/migrations" defaultMigrationsDir :: FilePath data MigrationDetails MigrationDetails :: FilePath -> String -> String -> MigrationDetails migrationPath :: MigrationDetails -> FilePath migrationVersion :: MigrationDetails -> String migrationName :: MigrationDetails -> String instance Show MigrationDetails -- | Utility function for describing a table in the database. module Database.PostgreSQL.Describe data ColumnInfo ColumnInfo :: !Int16 -> ByteString -> !TypeInfo -> !Bool -> !Bool -> !Bool -> !(Maybe ByteString) -> ColumnInfo -- | Internal column number used by PostgreSQL. Generally these will be -- consecutive starting from 1, but this may not be the case if you have -- altered a table to delete columns. colNum :: ColumnInfo -> !Int16 -- | Name of the column colName :: ColumnInfo -> ByteString -- | Type of the column colType :: ColumnInfo -> !TypeInfo -- | If True, the database cannot contain null. (This constraint -- should always be accurate.) colNotNull :: ColumnInfo -> !Bool -- | True if this column (and only this column) constitutes the -- primary key of the table. Always False if the primary key -- comprises multiple columns (even if this is one of those columns). colPrimary :: ColumnInfo -> !Bool -- | True if there is a uniqueness constraint on this column. Not -- True if this column is part of a uniqueness constraint -- involving multiple columns. (Such multi-column uniqueness constraints -- are not reported by this interface.) colUnique :: ColumnInfo -> !Bool -- | If this there is a foreign key constraint on this column (and the -- constraint does not span multiple columns), report the table -- referenced by this column. colReferences :: ColumnInfo -> !(Maybe ByteString) -- | Returns a list of ColumnInfo structures for a particular table. -- Not all information about a table is returned. In particular, -- constraints that span columns are ignored. describeTable :: Connection -> ByteString -> IO [ColumnInfo] instance Show ColumnInfo -- | Functions for initializing self-contained local postgreSQL database -- clusters (useful in development more than production). module Database.PostgreSQL.Devel -- | Create a directory for a local database cluster entirely -- self-contained within one directory. This is accomplished by creating -- a new PostgreSQL database cluster in the directory and setting the -- following configuration options in postgresql.conf: -- --
-- configLocalDB dbpath [("fsync", "fsync = off")]
--
--
-- Note that the second element of each pair is the complete
-- configuration line. It is not correct to say:
--
--
-- configLocalDB dbpath [("fsync", "off")] -- INCORRECT
--
configLocalDB :: FilePath -> [(String, String)] -> IO ()
-- | Start a local database if the server is not already running.
-- Otherwise, does nothing, but returns a ConnectInfo in either
-- case. The database server will continue running after the current
-- process exits (but see stopLocalDB).
startLocalDB :: FilePath -> IO ConnectInfo
-- | A combination of createLocalDB and startLocalDB.
--
-- The parameter is a PostgreSQL data directory. If the directory is
-- empty or does not exist, this function creates a new database cluster
-- (via createLocalDB). Then, if a database server is not already
-- running for the directory, starts a server. No matter what, returns a
-- ConnectInfo that will connect to the server running on this
-- local database.
--
-- Note that if initLocalDB starts a postgres server, the server
-- process will continue running after the process that called
-- initLocalDB exits. This is normally fine. Since multiple
-- client processes may access the same PostgreSQL database, it makes
-- sense for the first client to start the database and no one to stop
-- it. See stopLocalDB if you wish to stop the server process
-- (which you should always do before deleting a test cluster). See also
-- withTempDB to create a temporary cluster for the purposes of
-- running a test suite.
initLocalDB :: FilePath -> IO ConnectInfo
-- | Stop the server for a local database cluster entirely self-contained
-- within one directory. You must call this before deleting the
-- directory, or else stray postgres processes will linger forever. If
-- the argument is the empty string, looks for the database directory in
-- the PGDATA environment variable.
stopLocalDB :: FilePath -> IO ()
-- | Set environment variables to make a local database cluster the
-- default. Also returns shell commands you can eval or cut-and-paste
-- into your shell to make pg_ctl and psql access a
-- local database cluster.
setLocalDB :: FilePath -> IO String
-- | Run a function with a completely fresh database cluster that gets
-- deleted on return. Since the entire database is blown away when the
-- function returns, withTempDB is obviously only useful for
-- test suites.
withTempDB :: (ConnectInfo -> IO a) -> IO a
-- | Reset a connection to its default state before re-cycling it for
-- another thread or request.
resetConnection :: Connection -> IO ()
module Data.RequireSelector
-- | The point of this class is to ensure that you are using data types
-- defined with record selectors (i.e., data Foo = Foo { unFoo :: Int
-- } as opposed to data Foo = Foo Int).
--
-- Unfortunately, GHC.Generics makes the NoSelector type a
-- member of the Selector class. Hence, if you want to ensure a
-- type a is not NoSelector, use the context
-- (RequireSelector a) =>.
--
-- If you see a compilation error involving RequireSelector or
-- IntentionallyCauseError, it means you failed to define one of
-- your datatypes using record selector syntax.
class RequireSelector a
instance RequireSelector a
instance IntentionallyCauseError NoSelector => RequireSelector NoSelector
-- | The main database ORM interface. This module contains functionality
-- for moving a Haskell data structure in and out of a database table.
--
-- The most important feature is the Model class, which encodes a
-- typed database interface (i.e., the ORM layer). This class has a
-- default implementation for types that are members of the
-- Generic class (using GHC's DeriveGeneric extension),
-- provided the following conditions hold:
--
--
-- data MyType = MyType { myKey :: !DBKey
-- , myName :: !S.ByteString
-- , myCamelCase :: !Int
-- , ...
-- } deriving (Show, Generic)
-- instance Model MyType
--
--
-- The default modelInfo method is called defaultModelInfo.
-- You may wish to use almost all of the defaults, but tweak a few
-- things. This is easily accomplished by overriding a few fields of the
-- default structure. For example, suppose your database columns use
-- exactly the same name as your Haskell field names, but the name of
-- your database table is not the same as the name of the Haskell data
-- type. You can override the database table name (field
-- modelTable) as follows:
--
--
-- instance Model MyType where
-- modelInfo = defaultModelInfo { modelTable = "my_type" }
--
--
-- Finally, if you dislike the conventions followed by
-- defaultModelInfo, you can simply implement an alternate
-- pattern. An example of this is underscoreModelInfo, which
-- strips a prefix off every field name and converts everything from
-- camel-case to underscore notation:
--
-- -- instance Model MyType where -- modelInfo = underscoreModelInfo "my" ---- -- The above code will associate MyType with a database table -- my_type having column names key, name, -- camel_case, etc. -- -- You can implement other patterns like underscoreModelInfo by -- calling defaultModelInfo and modifying the results. -- Alternatively, you can directly call the lower-level functions from -- which defaultModelInfo is built (defaultModelTable, -- defaultModelColumns, defaultModelGetPrimaryKey). module Database.PostgreSQL.ORM.Model -- | The class of data types that represent a database table. This class -- conveys information necessary to move a Haskell data structure in and -- out of a database table. The most important field is modelInfo, -- which describes the database table and column names. modelInfo -- has a reasonable default implementation for types that are members of -- the Generic class (using GHC's DeriveGeneric -- extension), provided the following conditions hold: -- --
-- data MyType = MyType { myKey :: !DBKey
-- , myName :: !S.ByteString
-- , myCamelCase :: !Int
-- , ...
-- } deriving (Show, Generic)
-- instance Model MyType
--
--
-- The default modelInfo method is called defaultModelInfo.
-- You may wish to use almost all of the defaults, but tweak a few
-- things. This is easily accomplished by overriding a few fields of the
-- default structure. For example, suppose your database columns use
-- exactly the same name as your Haskell field names, but the name of
-- your database table is not the same as the name of the Haskell data
-- type. You can override the database table name (field
-- modelTable) as follows:
--
--
-- instance Model MyType where
-- modelInfo = defaultModelInfo { modelTable = "my_type" }
--
--
-- Finally, if you dislike the conventions followed by
-- defaultModelInfo, you can simply implement an alternate
-- pattern. An example of this is underscoreModelInfo, which
-- strips a prefix off every field name and converts everything from
-- camel-case to underscore notation:
--
-- -- instance Model MyType where -- modelInfo = underscoreModelInfo "my" ---- -- The above code will associate MyType with a database table -- my_type having column names key, name, -- camel_case, etc. -- -- You can implement other patterns like underscoreModelInfo by -- calling defaultModelInfo and modifying the results. -- Alternatively, you can directly call the lower-level functions from -- which defaultModelInfo is built (defaultModelTable, -- defaultModelColumns, defaultModelGetPrimaryKey). class Model a where modelInfo = defaultModelInfo modelIdentifiers = defaultModelIdentifiers modelInfo modelRead = defaultFromRow modelWrite = defaultModelWrite modelQueries = defaultModelQueries modelIdentifiers modelCreateInfo = emptyModelCreateInfo modelValid = const [] modelInfo :: Model a => ModelInfo a modelIdentifiers :: Model a => ModelIdentifiers a modelRead :: Model a => RowParser a modelWrite :: Model a => a -> [Action] modelQueries :: Model a => ModelQueries a modelCreateInfo :: Model a => ModelCreateInfo a modelValid :: Model a => a -> [InvalidError] -- | A ModelInfo T contains the information necessary for mapping -- T to a database table. Each Model type has a -- single ModelInfo associated with it, accessible through the -- modelInfo method of the Model class. Note the table and -- column names must all be unquoted in this data structure, as they will -- later be quoted using quoteIdent by the modelIdentifiers -- method. data ModelInfo a ModelInfo :: !ByteString -> ![ByteString] -> !Int -> !(a -> DBKey) -> ModelInfo a -- | The name of the database table corresponding to this model. The -- default modelInfo instance uses defaultModelTable, which -- is the name of your data type with the first letter downcased. modelTable :: ModelInfo a -> !ByteString -- | The names of the database columns corresponding to fields of this -- model. The column names should appear in the order in which the fields -- are defined in the Haskell data type a (which should also be -- the order in which modelRead parses them to an a and -- modelWrite marshalls them). -- -- Note that all queries generated by the library specify explicit column -- names. Hence the order of columns does not need to match their order -- in the database table. They should instead match the order of fields -- in the Haskell data structure. -- -- The default, given by defaultModelColumns, is to use the -- Haskell field names for a. This default will fail to compile -- if a is not defined using record syntax. modelColumns :: ModelInfo a -> ![ByteString] -- | The 0-based index of the primary key column in modelColumns. -- This should be 0 when your data structure's first field is its -- DBKey (highly recommended, and required by -- defaultModelGetPrimaryKey). If you customize this field, you -- must also customize modelGetPrimaryKey--no check is made that -- the two are consistent. modelPrimaryColumn :: ModelInfo a -> !Int -- | Return the primary key of a particular model instance. If you -- customize this field, you must also customize -- modelPrimaryColumn--no check is made that the two are -- consistent. modelGetPrimaryKey :: ModelInfo a -> !(a -> DBKey) -- | SQL table and column identifiers that should be copied verbatim into -- queries. For normal models, these will simply be quoted versions of -- the fields in the corresponding ModelInfo. However, for special -- cases, the fields of this structure can contain unquoted SQL including -- JOIN keywords. In the case of joins, different elements of -- modelQColumns may be qualified by different table names. -- -- Note that modelQColumns and modelQPrimaryColumn both -- contain table-qualified names (e.g., "\"my_type\".\"key\""), -- while modelQWriteColumns contains only the quoted column names. data ModelIdentifiers a ModelIdentifiers :: !ByteString -> ![ByteString] -> ByteString -> [ByteString] -> !(Maybe ByteString) -> !(Maybe ByteString) -> ModelIdentifiers a -- | Literal SQL for the name of the table. modelQTable :: ModelIdentifiers a -> !ByteString -- | Literal SQL for each, table-qualified column. modelQColumns :: ModelIdentifiers a -> ![ByteString] -- | Literal SQL for the model's table-qualified primary key column. modelQPrimaryColumn :: ModelIdentifiers a -> ByteString -- | Literal SQL for all the columns except the primary key. These are the -- columns that should be included in an INSERT or -- UPDATE. Note that unlike the other fields, these column names -- should not be table-qualified. modelQWriteColumns :: ModelIdentifiers a -> [ByteString] -- | When all columns in modelQColumns are qualified by the same -- table name, this field contains Just the table name. For the -- :. type (in which different columns have different table -- qualifications), this field is Nothing. -- -- For normal models, this field will be identical to modelQTable. -- However, for As models, modelQTable will contain -- unquoted SQL such as "\"MyType\" AS \"my_alias\"", in which -- case modelQualifier will contain Just -- "\"my_alias\"". modelQualifier :: ModelIdentifiers a -> !(Maybe ByteString) -- | The original, unquoted name of the table representing the model in the -- database. Ordinarily, this should be the same as modelTable in -- ModelInfo, but in the case of As aliases, the -- modelTable is an alias, and modelOrigTable is the -- original table. Nothing for joins. modelOrigTable :: ModelIdentifiers a -> !(Maybe ByteString) -- | Standard CRUD (create/read/update/delete) queries on a model. data ModelQueries a ModelQueries :: !Query -> !Query -> !Query -> !Query -> ModelQueries a -- | A query template for looking up a model by its primary key. Expects a -- single query parameter, namely the DBKey or DBRef being -- looked up. modelLookupQuery :: ModelQueries a -> !Query -- | A query template for updating an existing Model in the -- database. Expects as query parameters a value for every column of the -- model except the primary key, followed by the primary key. (The -- primary key is not written to the database, just used to select the -- row to change.) modelUpdateQuery :: ModelQueries a -> !Query -- | A query template for inserting a new Model in the database. The -- query parameters are values for all columns except the primary -- key. The query returns the full row as stored in the database -- (including the values of fields, such as the primary key, that have -- been chosen by the database server). modelInsertQuery :: ModelQueries a -> !Query -- | A query template for deleting a Model from the database. Should -- have a single query parameter, namely the DBKey of the row to -- delete. modelDeleteQuery :: ModelQueries a -> !Query -- | An alternate Model pattern in which Haskell type and field -- names are converted from camel-case to underscore notation. The first -- argument is a prefix to be removed from field names (since Haskell -- requires field names to be unique across data types, while SQL allows -- the same column names to be used in different tables). -- -- For example: -- --
-- data Bar = Bar {
-- barId :: !DBKey
-- , barNameOfBar :: !String
-- , barParent :: !(Maybe (DBRef Bar))
-- } deriving (Show, Generic)
--
-- instance Model Bar where modelInfo = underscoreModelInfo "bar"
--
--
-- would associate type Bar with a database table called
-- bar with fields id, name_of_bar, and
-- parent.
underscoreModelInfo :: (Generic a, GToRow (Rep a), GFromRow (Rep a), GPrimaryKey0 (Rep a), GColumns (Rep a), GDatatypeName (Rep a)) => ByteString -> ModelInfo a
-- | A type large enough to hold database primary keys. Do not use this
-- type directly in your data structures. Use DBKey to hold a
-- Model's primary key and DBRef to reference the primary
-- key of another model.
type DBKeyType = Int64
-- | The type of the Haskell data structure field containing a model's
-- primary key.
--
-- Every Model must have exactly one DBKey, and the
-- DBKey must be the Model's very first field in the
-- Haskel data type definition. (The ordering is enforced by
-- defaultModelGetPrimaryKey, which, through use of the
-- DeriveGeneric extension, fails to compile when the first
-- field is not a DBKey.)
--
-- Each Model stored in the database should have a unique non-null
-- primary key. However, the key is determined at the time the
-- Model is inserted into the database. While you are constructing
-- a new Model to insert, you will not have its key. Hence, you
-- should use the value NullKey to let the database chose the
-- key.
--
-- If you wish to store a Model's primary key as a reference in
-- another Model, do not copy the DBKey structure. Use
-- mkDBRef to convert the Model's primary key to a foreign
-- key reference.
data DBKey
DBKey :: !DBKeyType -> DBKey
NullKey :: DBKey
-- | Returns True when a DBKey is NullKey.
isNullKey :: DBKey -> Bool
-- | A DBRef T represents a many-to-one relationship between
-- tables. For example, if type A contains a DBRef B,
-- then each B is associated with many A's. By
-- contrast, a DBRefUnique represents a one-to-one
-- relationship.
--
-- DBRef is a type alias of kind * -> *. The type
-- DBRef T references an instance of type T by the
-- primary key of its database row. The type argument T should
-- be an instance of Model.
type DBRef = GDBRef NormalRef
-- | A DBRefUnique T represents a one-to-one relationship between
-- types. For example, if type A contains a DBRefUnique
-- B, then each A is associated with one (or at most one)
-- B, and each B has one (or at most one) A
-- associated with it.
--
-- By contrast, a DBRef represents a many-to-one
-- relationship.
type DBRefUnique = GDBRef UniqueRef
-- | Many operations can take either a DBRef or a DBRefUnique
-- (both of which consist internally of a DBKeyType). Hence, these
-- two types are just type aliases to a generalized reference type
-- GDBRef, where GDBRef's first type argument,
-- reftype, is a phantom type denoting the flavor of reference
-- (NormalRef or UniqueRef).
newtype GDBRef reftype table
DBRef :: DBKeyType -> GDBRef reftype table
-- | Create a reference to the primary key of a Model, suitable for
-- storing in a DBRef or DBRefUnique field of a different
-- Model.
mkDBRef :: Model a => a -> GDBRef rt a
-- | Dump an entire model. Useful for development and debugging only, as
-- every row will be read into memory before the function returns.
--
-- Note that unlike the other primary model operations, it is OK to call
-- findAll even on degenerate models such as As and
-- :..
findAll :: Model r => Connection -> IO [r]
-- | Follow a DBRef or DBRefUnique and fetch the target row
-- from the database into a Model type r.
findRow :: Model r => Connection -> GDBRef rt r -> IO (Maybe r)
-- | Like trySave but instead of returning an Either, throws
-- a ValidationError if the Model is invalid.
save :: Model r => Connection -> r -> IO r
-- | Write a Model to the database. If the primary key is
-- NullKey, the item is written with an INSERT query,
-- read back from the database, and returned with its primary key filled
-- in. If the primary key is not NullKey, then the Model is
-- written with an UPDATE query and returned as-is.
--
-- If the Model is invalid (i.e. the return value of
-- modelValid is non-empty), a list of InvalidError is
-- returned instead.
trySave :: Model r => Connection -> r -> IO (Either [InvalidError] r)
-- | Remove the row corresponding to a particular data structure from the
-- database. This function only looks at the primary key in the data
-- structure. It is an error to call this function if the primary key is
-- not set.
destroy :: Model a => Connection -> a -> IO ()
-- | Remove a row from the database without fetching it first.
destroyByRef :: Model a => Connection -> GDBRef rt a -> IO ()
-- | Lookup the modelTable of a Model (modelName _ =
-- modelTable (modelInfo :: ModelInfo a)).
modelName :: Model a => a -> ByteString
-- | Lookup the primary key of a Model.
primaryKey :: Model a => a -> DBKey
-- | Generate a SQL SELECT statement with no WHERE
-- predicate. For example, defaultModelLookupQuery consists of
-- modelSelectFragment followed by "WHERE
-- primary-key = ?".
modelSelectFragment :: ModelIdentifiers a -> ByteString
-- | A newtype wrapper in the FromRow class, permitting every model
-- to be used as the result of a database query.
newtype LookupRow a
LookupRow :: a -> LookupRow a
lookupRow :: LookupRow a -> a
-- | A newtype wrapper in the ToRow class, which marshalls every
-- field except the primary key, followed by the primary key. For use
-- with modelUpdateQuery.
newtype UpdateRow a
UpdateRow :: a -> UpdateRow a
-- | A newtype wrapper in the ToRow class, which marshalls every
-- field except the primary key. For use with modelInsertQuery.
newtype InsertRow a
InsertRow :: a -> InsertRow a
-- | The newtype As can be wrapped around an existing type to give
-- it a table name alias in a query. This is necessary when a model is
-- being joined with itself, to distinguish the two joined instances of
-- the same table.
--
-- For example:
--
--
-- {-# LANGUAGE OverloadedStrings #-}
--
-- data X = X
-- instance RowAlias X where rowAliasName = const "x"
--
-- ...
-- r <- dbSelect c $ addWhere_ "bar.bar_key = x.bar_parent" modelDBSelect
-- :: IO [Bar :. As X Bar]
--
newtype As alias row
As :: row -> As alias row
unAs :: As alias row -> row
-- | fromAs extracts the row from an As alias
-- row, but constrains the type of alias to be the same as
-- its first argument (which is non-strict). This can save you from
-- explicitly specifying types. For example:
--
-- -- data X = X deriving (Generic) -- instance RowAlias X where rowAliasName = const "x" -- -- ... -- r <- map (\(b1 :. b2) -> (b1, fromAs X b2)) <$> -- dbSelect c $ addWhere \"bar.bar_key = x.bar_parent\" modelDBSelect --fromAs :: alias -> As alias row -> row -- | A type-restricted wrapper around the As constructor, under the -- same rationale as fromAs. Not strict in its first argument. toAs :: alias -> row -> As alias row -- | The class of types that can be used as tags in as As alias. -- Such types should be unit types--in other words, have exactly one -- constructor where the constructor is nullary (take no arguments). The -- reason for this class is that the Model instance for As -- requires a way to extract the name of the row alias without having a -- concrete instance of the type. This is provided by the -- rowAliasName method (which must be non-strict). class RowAlias a where rowAliasName _ = fromString $ caseFold $ gUnitTypeName . from $ a where caseFold (h : t) = toLower h : t caseFold [] = [] a = undefined :: a rowAliasName :: RowAlias a => g a row -> ByteString -- | The default definition of modelInfo. See the documentation at -- Model for more information. Sets modelTable to the name -- of the type with the first character converted to lower-case. Sets -- modelColumns to the names of the Haskell field selectors. Sets -- modelPrimaryColumn to 0 and extracts the first field -- of the structure for modelGetPrimaryKey. Will fail to compile -- unless the data structure is defined with record syntax and that its -- first field is of type DBKey. -- -- Note that defaults for the individual fields are available in separate -- functions (e.g., defaultModelTable) with fewer class -- requirements in the context, in case you want to make piecemeal use of -- defaults. The default for modelPrimaryColumn is 0. If you -- overwrite that, you will need to overwrite modelGetPrimaryKey -- as well (and likely vice versa). defaultModelInfo :: (Generic a, GDatatypeName (Rep a), GColumns (Rep a), GPrimaryKey0 (Rep a)) => ModelInfo a -- | The default name of the database table corresponding to a Haskell -- type. The default is the same as the type name with the first letter -- converted to lower-case. (The rationale is that Haskell requires types -- to start with a capital letter, but all-lower-case table names are -- easier to use in queries because PostgreSQL generally does not require -- them to be quoted.) defaultModelTable :: (Generic a, GDatatypeName (Rep a)) => a -> ByteString -- | Returns the Haskell field names in a data structure. defaultModelColumns :: (Generic a, GColumns (Rep a)) => a -> [ByteString] -- | Extract the primary key of type DBKey from a model when the -- DBKey is the first element of the data structure. Fails to -- compile if the first field is not of type DBKey. defaultModelGetPrimaryKey :: (Generic a, GPrimaryKey0 (Rep a)) => a -> DBKey -- | The default simply quotes the modelInfo and modelColumns -- fields of ModelInfo using quoteIdent. defaultModelIdentifiers :: ModelInfo a -> ModelIdentifiers a -- | Returns a series of Actions serializing each field of a data -- structure (in the order of the Haskell datatype definition), -- except the primary key, since the primary key should never be -- written to a database. Every field must be an instance of -- ToField. defaultModelWrite :: (Model a, Generic a, GToRow (Rep a)) => a -> [Action] -- | The default value of modelQueries. defaultModelQueries :: ModelIdentifiers a -> ModelQueries a -- | Default SQL lookup query for a model. defaultModelLookupQuery :: ModelIdentifiers a -> Query -- | Default SQL update query for a model. defaultModelUpdateQuery :: ModelIdentifiers a -> Query -- | Default SQL insert query for a model. defaultModelInsertQuery :: ModelIdentifiers a -> Query -- | Default SQL delete query for a model. defaultModelDeleteQuery :: ModelIdentifiers a -> Query -- | Quote an identifier such as a table or column name using double-quote -- characters. Note this has nothing to do with quoting values, -- which must be quoted using single quotes. (Anyway, all values should -- be quoted by query or fmtSql.) This function uses a -- unicode escape sequence to escape '?' characters, which would -- otherwise be expanded by query, formatQuery, or -- fmtSql. -- --
-- >>> S8.putStrLn $ quoteIdent "hello \"world\"!" -- "hello ""world""!" -- -- >>> S8.putStrLn $ quoteIdent "hello \"world\"?" -- U&"hello ""world""\003f" ---- -- Note that this quoting function is correct only if -- client_encoding is SQL_ASCII, client_coding -- is UTF8, or the identifier contains no multi-byte characters. -- For other coding schemes, this function may erroneously duplicate -- bytes that look like quote characters but are actually part of a -- multi-byte character code. In such cases, maliciously crafted -- identifiers will, even after quoting, allow injection of arbitrary SQL -- commands to the server. -- -- The upshot is that it is unwise to use this function on identifiers -- provided by untrustworthy sources. Note this is true anyway, -- regardless of client_encoding setting, because certain -- "system column" names (e.g., oid, tableoid, -- xmin, cmin, xmax, cmax, -- ctid) are likely to produce unexpected results even when -- properly quoted. -- -- See Id for a convenient way to include quoted identifiers in -- parameter lists. quoteIdent :: ByteString -> ByteString -- | Phantom type for instantiating GDBRef that represents a -- one-to-many relationship between tables. data NormalRef NormalRef :: NormalRef -- | Phantom type for instantiating GDBRef that represents a -- one-to-one relationship between tables. data UniqueRef UniqueRef :: UniqueRef -- | Extra information for Database.PostgreSQL.ORM.CreateTable. You -- probably don't need to use this. data ModelCreateInfo a ModelCreateInfo :: ![(ByteString, ByteString)] -> !ByteString -> ModelCreateInfo a -- | A list of (column-name, type) pairs for which you want to override the -- default. modelCreateColumnTypeExceptions :: ModelCreateInfo a -> ![(ByteString, ByteString)] -- | Extra constraints to stick at the end of the CREATE TABLE -- statement. modelCreateExtraConstraints :: ModelCreateInfo a -> !ByteString -- | A ModelCreateInfo that doesn't imply any extra constraints or -- exceptions. emptyModelCreateInfo :: ModelCreateInfo a -- | This function provides a fromRow function for Generic -- types, suitable as a default of the FromRow class. This module -- uses it as the default implementation of modelRead. defaultFromRow :: (Generic a, GFromRow (Rep a)) => RowParser a -- | This function provides a toRow function for Generic -- types that marshalls each field of the data type in the order in which -- it appears in the type definition. This function is not a -- suitable implementation of modelWrite (since it marshals the -- primary key, which is not supposed to be written). However, it is -- required internally by defaultModelWrite, and exposed in the -- unlikely event it is of use to alternate generic modelWrite -- functions. You probably don't want to call this function. defaultToRow :: (Generic a, GToRow (Rep a)) => a -> [Action] -- | Print to stdout the query statement. printq :: Query -> IO () -- | This class extracts the first field in a data structure when the field -- is of type DBKey. If you get a compilation error because of -- this class, then move the DBKey first in your data structure. class GPrimaryKey0 f -- | This class extracts the field names of a Haskell data structure. Only -- defined for types with a single constructor that uses record syntax. class GColumns f -- | This class returns the name of a datatype. class GDatatypeName f class GFromRow f class GToRow f instance Typeable DBKey instance Typeable2 GDBRef instance Typeable NormalRef instance Typeable UniqueRef instance Data DBKey instance Eq (GDBRef reftype table) instance (Data reftype, Data table) => Data (GDBRef reftype table) instance Num (GDBRef reftype table) instance Integral (GDBRef reftype table) instance Real (GDBRef reftype table) instance Ord (GDBRef reftype table) instance Enum (GDBRef reftype table) instance Bounded (GDBRef reftype table) instance Show NormalRef instance Data NormalRef instance Show UniqueRef instance Data UniqueRef instance Show (ModelIdentifiers a) instance Show (ModelQueries a) instance Show (ModelCreateInfo a) instance Show a => Show (LookupRow a) instance Show a => Show (InsertRow a) instance Show a => Show (UpdateRow a) instance Model a => ToRow (UpdateRow a) instance Model a => ToRow (InsertRow a) instance Model a => FromRow (LookupRow a) instance (Model a, RowAlias as) => Model (As as a) instance (RowAlias alias, Show row) => Show (As alias row) instance (Datatype c, GUnitType f) => GUnitType (D1 c f) instance GUnitType V1 instance GUnitType (C1 c U1) instance (Model a, Model b) => Model (a :. b) instance (FromField a, FromField b, FromField c, FromField d, FromField e) => Model (a, b, c, d, e) instance (FromField a, FromField b, FromField c, FromField d) => Model (a, b, c, d) instance (FromField a, FromField b, FromField c) => Model (a, b, c) instance (FromField a, FromField b) => Model (a, b) instance FromField t => Model [t] instance FromField t => Model (Only t) instance GToRow f => GToRow (M1 i c f) instance (GToRow a, GToRow b) => GToRow (a :*: b) instance ToField c => GToRow (K1 i c) instance GToRow U1 instance GFromRow f => GFromRow (M1 i c f) instance (GFromRow a, GFromRow b) => GFromRow (a :*: b) instance FromField c => GFromRow (K1 i c) instance GFromRow U1 instance GPrimaryKey0 f => GPrimaryKey0 (D1 c f) instance GPrimaryKey0 f => GPrimaryKey0 (C1 c f) instance GPrimaryKey0 a => GPrimaryKey0 (a :*: b) instance RequireSelector c => GPrimaryKey0 (S1 c (K1 i DBKey)) instance GColumns f => GColumns (M1 D c f) instance GColumns f => GColumns (M1 C c f) instance (GColumns a, GColumns b) => GColumns (a :*: b) instance (Selector c, RequireSelector c) => GColumns (M1 S c f) instance GColumns U1 instance Datatype c => GDatatypeName (D1 c f) instance Show (ModelInfo a) instance ToField (GDBRef rt t) instance FromField (GDBRef rt t) instance Model t => Read (GDBRef rt t) instance Model t => Show (GDBRef rt t) instance ToField DBKey instance FromField DBKey instance Show DBKey instance Ord DBKey instance Eq DBKey module Database.PostgreSQL.ORM.DBSelect -- | A deconstructed SQL select statement that allows easier manipulation -- of individual terms. Several functions are provided to combine the -- selFields, selFrom, and selWhere clauses of -- muliple DBSelect structures. Other clauses may be discarded -- when combining queries with join operations. Hence it is advisable to -- set the other clauses at the end (or, if you set these fields, to -- collapse your DBSelect structure into a subquery using -- dbProject'). data DBSelect a DBSelect :: !Query -> !Query -> Query -> !FromClause -> !Query -> !Query -> !Query -> !Query -> !Query -> !Query -> !Query -> DBSelect a selWith :: DBSelect a -> !Query -- | By default "SELECT", but might usefully be set to something -- else such as "SELECT DISTINCT" in some situations. selSelectKeyword :: DBSelect a -> !Query selFields :: DBSelect a -> Query selFrom :: DBSelect a -> !FromClause -- | Empty by default, but set to "WHERE" if any WHERE -- clauses are added to the selWhere field. selWhereKeyword :: DBSelect a -> !Query selWhere :: DBSelect a -> !Query selGroupBy :: DBSelect a -> !Query selHaving :: DBSelect a -> !Query selOrderBy :: DBSelect a -> !Query selLimit :: DBSelect a -> !Query selOffset :: DBSelect a -> !Query -- | As it's name would suggest, a FromClause is the part of a -- query between the FROM keyword and the WHERE -- keyword. It can consist of simple table names, JOIN -- operations, and parenthesized subqueries. -- -- From clauses are represented in a more structured way than the other -- fields so as to allow the possibility of collapsing join relations. -- For instance, given a DBSelect (A :. B) and a -- DBSelect (B :. C), it is desirable to be able to -- generate a DBSelect (A :. B :. C) in which each pair -- of terms involving B in the three-way relation is constrained -- according to the original two queries. This functionality is provided -- by dbNest and dbChain, but it requires the ability to -- locate and replace the instance of type B in one -- DBSelect with the FromClause of the other -- DBSelect. -- -- The fcCanonical field is a canonical name of each type, which -- by convention is the quoted and fully-qualified table name. Comparing -- fcCanonical is somewhat of a hack, and happens entirely at -- runtime. It would be nicer to do this at compile time, but doing so -- would require language extensions such as GADTs of -- FunctionalDependencies. data FromClause FromModel :: !Query -> !ByteString -> FromClause -- | Verbatim SQL for a table, table AS alias, or parenthesized -- subquery. fcVerbatim :: FromClause -> !Query -- | Canonical name of the table or join relation represented by this term. -- For JOIN terms, this is always the CROSS JOIN of the -- canonical names of fcLeft and fcRight. This means one -- can locate a join given only it's type (e.g., the canonical name for -- A :. B is always "a CROSS JOIN b"), but it does mean -- you have to be careful not accidentally to merge two different joins -- on the same types. For this reason it may be safest always to have -- type b be a single table in dbNest and dbChain. fcCanonical :: FromClause -> !ByteString FromJoin :: !FromClause -> !Query -> !FromClause -> !Query -> !ByteString -> FromClause fcLeft :: FromClause -> !FromClause -- | Usually "JOIN" fcJoinOp :: FromClause -> !Query fcRight :: FromClause -> !FromClause -- | ON or USING clause (or empty) fcOnClause :: FromClause -> !Query -- | Canonical name of the table or join relation represented by this term. -- For JOIN terms, this is always the CROSS JOIN of the -- canonical names of fcLeft and fcRight. This means one -- can locate a join given only it's type (e.g., the canonical name for -- A :. B is always "a CROSS JOIN b"), but it does mean -- you have to be careful not accidentally to merge two different joins -- on the same types. For this reason it may be safest always to have -- type b be a single table in dbNest and dbChain. fcCanonical :: FromClause -> !ByteString -- | Run a DBSelect query on parameters. The number of '?' -- characters embedeed in various fields of the DBSelect must -- exactly match the number of fields in parameter type p. Note -- the order of arguments is such that the DBSelect can be -- pre-rendered and the parameters supplied later. Hence, you should use -- this version when the DBSelect is static. For dynamically -- modified DBSelect structures, you may prefer dbSelect. dbSelectParams :: (Model a, ToRow p) => DBSelect a -> Connection -> p -> IO [a] -- | Run a DBSelect query and return the resulting models. dbSelect :: Model a => Connection -> DBSelect a -> IO [a] -- | Turn a DBSelect into a Query suitable for the -- query or query_ functions. renderDBSelect :: DBSelect a -> Query -- | Create a Builder for a rendered version of a DBSelect. -- This can save one string copy if you want to embed one query inside -- another as a subquery, as done by dbProject', and thus need to -- parenthesize it. However, the function is probably not a useful for -- end users. buildDBSelect :: DBSelect a -> Builder -- | A DBSelect structure with keyword "SELECT" and -- everything else empty. emptyDBSelect :: DBSelect a -- | A DBSelect for one or more comma-separated expressions, rather -- than for a table. For example, to issue the query "SELECT -- lastval()": -- --
-- lastval :: DBSelect (Only DBKeyType) -- lastval = expressionDBSelect "lastval ()" -- -- ... -- [just_inserted_id] <- dbSelect conn lastval ---- -- On the other hand, for such a simple expression, you might as well -- call query_ directly. expressionDBSelect :: Model r => Query -> DBSelect r -- | A DBSelect that returns all rows of a model. modelDBSelect :: Model a => DBSelect a -- | Create a join of the selFields, selFrom, and -- selWhere clauses of two DBSelect queries. Other fields -- are simply taken from the second DBSelect, meaning fields such -- as selWith, selGroupBy, and selOrderBy in the in -- the first DBSelect are entirely ignored. dbJoin :: (Model a, Model b) => DBSelect a -> Query -> DBSelect b -> Query -> DBSelect (a :. b) -- | A version of dbJoin that uses modelDBSelect for the -- joined tables. dbJoinModels :: (Model a, Model b) => Query -> Query -> DBSelect (a :. b) -- | Restrict the fields returned by a DBSelect to be those of a single -- Model a. It only makes sense to do this if a -- is part of something_containing_a, but no static check is -- performed that this is the case. If you dbProject a type that -- doesn't make sense, you will get a runtime error from a failed -- database query. dbProject :: Model a => DBSelect something_containing_a -> DBSelect a -- | Like dbProject, but renders the entire input DBSelect as -- a subquery. Hence, you can no longer mention fields of models other -- than a that might be involved in joins. The two advantages of -- this approach are 1) that you can once again join to tables that were -- part of the original query without worrying about row aliases, and 2) -- that all terms of the DBSelect will be faithrully rendered into -- the subquery (whereas otherwise they could get dropped by join -- operations). Generally you will still want to use dbProject, -- but dbProject' is available when needed. dbProject' :: Model a => DBSelect something_containing_a -> DBSelect a -- | Nest two type-compatible JOIN queries. As with dbJoin, -- fields of the first JOIN (the DBSelect (a :. -- b)) other than selFields, selFrom, and -- selWhere are entirely ignored. dbNest :: (Model a, Model b) => DBSelect (a :. b) -> DBSelect (b :. c) -> DBSelect (a :. (b :. c)) -- | Like dbNest, but projects away the middle type b. dbChain :: (Model a, Model b, Model c) => DBSelect (a :. b) -> DBSelect (b :. c) -> DBSelect (a :. c) -- | Add a where clause verbatim to a DBSelect. The clause must -- not contain the WHERE keyword (which is added -- automatically by addWhere_ if needed). If the -- DBSelect has existing WHERE clauses, the new clause -- is appended with AND. If the query contains any '?' -- characters, they will be rendered into the query and matching -- parameters will later have to be filled in via a call to -- dbSelectParams. addWhere_ :: Query -> DBSelect a -> DBSelect a -- | Add a where clause, and pre-render parameters directly into the -- clause. The argument p must have exactly as many fields as -- there are '?' characters in the Query. Example: -- --
-- bars <- dbSelect c $ addWhere "bar_id = ?" (Only target_id) $ -- (modelDBSelect :: DBSelect Bar) --addWhere :: ToRow p => Query -> p -> DBSelect a -> DBSelect a -- | Set the ORDER BY clause of a DBSelect. Example: -- --
-- dbSelect c $ setOrderBy "\"employeeName\" DESC NULLS FIRST" $ -- modelDBSelect --setOrderBy :: Query -> DBSelect a -> DBSelect a -- | Set the LIMIT clause of a DBSelect. setLimit :: Int -> DBSelect a -> DBSelect a -- | Set the OFFSET clause of a DBSelect. setOffset :: Int -> DBSelect a -> DBSelect a -- | Add one or more comma-separated expressions to selFields that -- produce column values without any corresponding relation in the -- FROM clause. Type r is the type into which the -- expression is to be parsed. Generally this will be an instance of -- FromRow that is a degenerate model (e.g., Only, or a -- tuple). -- -- For example, to rank results by the field value and compute -- the fraction of overall value they contribute: -- --
-- r <- dbSelect c $ addExpression -- "rank() OVER (ORDER BY value), value::float4/SUM(value) OVER ()" -- modelDBSelect -- :: IO [Bar :. (Int, Double)] --addExpression :: Model r => Query -> DBSelect a -> DBSelect (a :. r) instance Show FromClause instance Generic (DBSelect a) instance Datatype D1DBSelect instance Constructor C1_0DBSelect instance Selector S1_0_0DBSelect instance Selector S1_0_1DBSelect instance Selector S1_0_2DBSelect instance Selector S1_0_3DBSelect instance Selector S1_0_4DBSelect instance Selector S1_0_5DBSelect instance Selector S1_0_6DBSelect instance Selector S1_0_7DBSelect instance Selector S1_0_8DBSelect instance Selector S1_0_9DBSelect instance Selector S1_0_10DBSelect instance GDBS f => GDBS (M1 i c f) instance (GDBS a, GDBS b) => GDBS (a :*: b) instance GDBS (K1 i FromClause) instance GDBS (K1 i Query) instance Show (DBSelect a) module Database.PostgreSQL.ORM.SqlType -- | The class of Haskell types that can be converted to and from a -- particular SQL type. For most instances, you only need to define -- sqlBaseType. class (ToField a, FromField a) => SqlType a where sqlType _ = (sqlBaseType (undefined :: a)) <> " NOT NULL" sqlBaseType :: SqlType a => a -> ByteString sqlType :: SqlType a => a -> ByteString -- | Retreive the Oid corresponding to a type. You can subsequently -- use the Oid to call getTypeInfo for more information -- on the type. getTypeOid :: Connection -> ByteString -> IO Oid instance Model a => SqlType (DBRefUnique a) instance Model a => SqlType (DBRef a) instance (Typeable a, SqlType a) => SqlType (Vector a) instance SqlType a => SqlType (Maybe a) instance SqlType DBKey instance SqlType (Binary ByteString) instance SqlType (Binary ByteString) instance SqlType String instance SqlType LocalTimestamp instance SqlType UTCTimestamp instance SqlType ZonedTimestamp instance SqlType Date instance SqlType Day instance SqlType UTCTime instance SqlType TimeOfDay instance SqlType ZonedTime instance SqlType LocalTime instance SqlType Oid instance SqlType Text instance SqlType Text instance SqlType ByteString instance SqlType ByteString instance SqlType Int64 instance SqlType Int32 instance SqlType Int16 instance SqlType Float instance SqlType Double instance SqlType Bool instance FromField ExtractTypeOid -- | Functions to extract a field of a particular type from a -- Generic data structure, when the data structure contains -- exactly one field of the given type. Only works for types with exactly -- one constructor (not variant types). -- -- An example of usage: -- --
-- data MyType = MyType { myString :: String -- position 0
-- , myInt :: Int -- position 1
-- , myBool :: Bool -- position 2
-- , myMaybeChar :: Maybe Char -- position 3
-- , myMaybeString :: Maybe String -- position 4
-- } deriving (Show, Generic)
--
-- myType :: MyType
-- myType = MyType "my type" 21 True Nothing (Just "maybe string")
--
--
-- -- >>> getFieldVal ExtractId myType :: String -- "my type" -- -- >>> getFieldVal ExtractId myType :: Int -- 21 -- -- >>> getFieldVal ExtractMaybe myType :: Maybe Char -- Nothing -- -- >>> getFieldVal ExtractMaybe myType :: Maybe Int -- Just 21 -- -- >>> getFieldVal ExtractMaybe myType :: Maybe String -- ambiguous -- <interactive>:5:1: Couldn't match type `THasMany' with `THasOne' -- -- >>> getFieldPos' ExtractId (undefined :: MyType) (undefined :: Bool) -- 2 -- -- >>> getFieldPos' ExtractMaybe (undefined :: MyType) (undefined :: Maybe Bool) -- 2 -- -- >>> getFieldPos' ExtractMaybe myType () -- No field has type () -- <interactive>:8:1: Couldn't match type `THasNone' with `THasOne' --module Data.GetField class (Generic a, GGetField f (Rep a) r THasOne) => GetField f a r getFieldVal :: GetField f a r => f r -> a -> r getFieldPos :: GetField f a r => f r -> a -> Int -- | An extractor that matches an exact field type. data ExtractId r ExtractId :: ExtractId r -- | An extractor that matches either type r or type Maybe -- r, and, in the former case, wraps Just around the value -- so as always to return type Maybe r. data ExtractMaybe r ExtractMaybe :: ExtractMaybe r -- | A variant of getFieldPos in which the type of the field is -- supplied as a non-strict argument. This may be easier than typecasting -- the extractor argument. For example, to extract the Int from a -- structure with a single Int field: -- --
-- getFieldPos' ExtractId myStruct (undefined :: Int) --getFieldPos' :: (Generic a, GGetField f (Rep a) r THasOne) => (f ()) -> a -> r -> Int -- | Exactly one matching field has been found. newtype THasOne a THasOne :: a -> THasOne a fromTHasOne :: THasOne a -> a -- | Zero matching fields have been found. data THasNone a THasNone :: THasNone a -- | More than one matching field has been found. newtype THasMany a THasMany :: [a] -> THasMany a fromTHasMany :: THasMany a -> [a] -- | Class of types used as tag arguments to gGetFieldVal and -- gGetFieldPos. f should be a new unit type of kind -- * -> *, used to designate the type of extraction you want. -- Then instances should be defined to transform each type a you -- want to extract to some type r, with g set to -- THasOne. -- -- For example, ExtractMaybe is a type to convert types a -- and Maybe a both to type Maybe a (i.e., type -- argument r is Maybe a). -- --
-- data ExtractMaybe a = ExtractMaybe -- instance Extractor ExtractMaybe a (Maybe a) THasOne where -- extract _ = THasOne . Just -- instance Extractor ExtractMaybe (Maybe a) (Maybe a) THasOne where -- extract _ = THasOne ---- -- Note that there is already a default general instance returning -- THasNone. Hence, you do not need to define one. Otherwise, you -- would have to define an overlapping instance such as: -- --
-- instance Extractor ExtractMaybe a b THasZero where -- Incorrect -- extract _ = THasNone ---- -- (Except the above wouldn't quite work anyway given the rules for -- overlapping instances.) So just assume that any instance you don't -- explicitly define for your Extractor will automatically fall -- back to THasNone. class Extractor f a r g | f a r -> g where extractCount fr a = gCount (extract fr a) extract :: Extractor f a r g => f r -> a -> g r extractCount :: Extractor f a r g => f r -> a -> (Int, [Int]) -- | Generlized extraction of a field from a Generic data structure. -- Argument rep should generally be the type Rep -- t for some data type t whose fields you want to extract. -- r is the result type you want back from the extraction. -- f should be defined such that there is an instance of -- Extractor f a r THasOne for each type a you -- want to convert to r and extract. class GGetField f rep r g | f rep r -> g gGetFieldVal :: GGetField f rep r g => f r -> rep p -> g r gGetFieldPos :: GGetField f rep r g => f r -> rep p -> (Int, [Int]) instance [overlap ok] [safe] Show a => Show (THasOne a) instance [overlap ok] [safe] Show (THasNone a) instance [overlap ok] [safe] Show a => Show (THasMany a) instance [overlap ok] [safe] Show (ExtractId r) instance [overlap ok] [safe] Extractor ExtractMaybe (Maybe a) (Maybe a) THasOne instance [overlap ok] [safe] Extractor ExtractMaybe a (Maybe a) THasOne instance [overlap ok] [safe] Extractor ExtractId a a THasOne instance [overlap ok] [safe] (Generic a, GGetField f (Rep a) r THasOne) => GetField f a r instance [overlap ok] [safe] GGetField f a r g => GGetField f (M1 i c a) r g instance [overlap ok] [safe] (GGetField f a1 r g1, GGetField f a2 r g2, GCombine g1 g2 g) => GGetField f (a1 :*: a2) r g instance [overlap ok] [safe] Extractor f c r g => GGetField f (K1 i c) r g instance [overlap ok] [safe] TypeGCast THasNone g => Extractor f a r g instance [overlap ok] [safe] GCount THasNone instance [overlap ok] [safe] GCount THasMany instance [overlap ok] [safe] GCount THasOne instance [overlap ok] [safe] GCombine THasMany THasOne THasMany instance [overlap ok] [safe] GCombine THasOne THasMany THasMany instance [overlap ok] [safe] GCombine THasMany THasNone THasMany instance [overlap ok] [safe] GCombine THasNone THasMany THasMany instance [overlap ok] [safe] GCombine THasMany THasMany THasMany instance [overlap ok] [safe] GCombine THasOne THasOne THasMany instance [overlap ok] [safe] GCombine THasNone THasNone THasNone instance [overlap ok] [safe] GCombine THasNone THasOne THasOne instance [overlap ok] [safe] GCombine THasOne THasNone THasOne instance [overlap ok] [safe] TypeGCast f f module Database.PostgreSQL.ORM.Association -- | A data structure representing a relationship between a model -- a and a model b. At a high level, an Association -- a b tells you how to find rows of type b given rows of -- type a. More concretely, this boils down to being able to -- make two types of query. -- --
-- findAssoc' ab c a = dbSelect c $ assocWhere ab a ---- -- But if the first argument is a static association, this function may -- be marginally faster because it pre-renders most of the query. findAssoc :: Model b => Association a b -> Connection -> a -> IO [b] -- | A common type of association is when one model contains a DBRef -- or DBRefUnique pointing to another model. In this case, the -- model containing the DBRef is known as the child, and -- the referenced model is known as the parent. -- -- Two pieces of information are required to describe a parent-child -- relationship: First, the field selector that extracts the Haskell -- DBRef from the haskell type child, and second the name -- of the database column that stores this DBRef field. -- -- For example, consider the following: -- --
-- data Author = Author {
-- authorId :: DBKey
-- } deriving (Show, Generic)
-- instance Model Author
--
-- data Post = Post {
-- postId :: DBKey
-- , postAuthorId :: DBRef Author
-- } deriving (Show, Generic)
-- instance Model Post
--
-- post_author_refinfo :: DBRefInfo Post Author
-- post_author_refinfo = DBRefInfo {
-- dbrefSelector = postAuthorId
-- , dbrefQColumn = "\"post\".\"postAuthorId\""
-- }
--
--
-- Note that the parent-child relationship described by a
-- GDBRefInfo is asymmetric, but bidirectional. When a
-- DBRefInfo child parent exists, the schema should
-- generally not permit the existence of a valid
-- DBRefInfo parent child structure. However, the
-- dbrefAssocs function generates Associations in both
-- directions from a single DBRefInfo.
--
-- Constructing such parent-child Associations requires knowing
-- how to extract primary keys from the parent type as well as
-- the name of the column storing primary keys in parent.
-- Fortunately, this information is already available from the
-- Model class, and thus does not need to be in the
-- GDBRefInfo. (Most functions on GDBRefInfos require
-- parent and child to be instances of Model.)
--
-- When your Models are instances of Generic (which will
-- usually be the case), a DBRefInfo structure can be computed
-- automatically by defaultDBRefInfo. This is the recommended way
-- to produce a GDBRefInfo. (Alternatively, see has and
-- belongsTo to make use of an entirely implicit
-- DBRefInfo.)
data GDBRefInfo reftype child parent
DBRefInfo :: !(child -> GDBRef reftype parent) -> !ByteString -> GDBRefInfo reftype child parent
-- | Field selector returning a reference.
dbrefSelector :: GDBRefInfo reftype child parent -> !(child -> GDBRef reftype parent)
-- | Literal SQL for the database column storing the reference. This should
-- be double-quoted and table-qualified, in case the column name is a
-- reserved keyword, contains capital letters, or conflicts with the name
-- of a column in the joined table. An example would be:
--
-- -- dbrefQColumn = "\"table_name\".\"column_name\"" --dbrefQColumn :: GDBRefInfo reftype child parent -> !ByteString -- | DBRefInfo is a type alias for the common case that the -- reference in a GDBRefInfo is a DBRef (as opposed to a -- DBRefUnique). The functions in this library do not care what -- type of reference is used. The type is generalized to -- GDBRefInfo just to make it easier to assign a selector to -- dbrefSelector when the selector returns a DBRefUnique. -- Note, however, that defaultDBRefInfo returns a DBRefInfo -- regardless of the flavor of reference actually encountered. type DBRefInfo = GDBRefInfo NormalRef -- | Creates a DBRefInfo from a model child that references -- parent. For this to work, the child type must be an -- instance of Generic and must contain exactly one field of the -- any of the following types: -- --
-- data Bar = Bar {
-- barId :: !DBKey
-- , barName :: !String
-- , barParent :: !(Maybe (DBRef Bar))
-- } deriving (Show, Generic)
-- instance Model Bar where modelInfo = underscoreModelInfo "bar"
--
-- data ParentBar = ParentBar
-- instance RowAlias ParentBar where rowAliasName _ = "parent_bar"
--
-- toParent :: Association Bar (As ParentBar Bar)
-- toParent = belongsTo
--
-- toChild :: Association (As ParentBar Bar) Bar
-- toChild = has
--
defaultDBRefInfo :: (Model child, Model parent, GetField ExtractRef child (DBRef parent)) => DBRefInfo child parent
-- | Generate both the child-parent and parent-child Associations
-- implied by a GDBRefInfo.
dbrefAssocs :: (Model child, Model parent) => GDBRefInfo rt child parent -> (Association child parent, Association parent child)
-- | Short for
--
-- -- snd $ dbrefAssocs defaultDBRefInfo ---- -- Note the inverse Association is given by belongsTo. For -- example, given the Author and Post models described -- in the documentation for GDBRefInfo, in which each -- Post references an Author, you might say: -- --
-- author_post :: Association Author Post -- author_post = has -- -- post_author :: Association Post Author -- post_author = belongsTo --has :: (Model child, Model parent, GetField ExtractRef child (DBRef parent)) => Association parent child -- | The inverse of has. Short for -- --
-- fst $ dbrefAssocs defaultDBRefInfo ---- -- See an example at has. belongsTo :: (Model child, Model parent, GetField ExtractRef child (DBRef parent)) => Association child parent -- | A data structure representing a dedicated join table in the database. -- A join table differs from a model in that rows do not have primary -- keys. Hence, model operations do not apply. Nonetheless a join table -- conveys information about a relationship between models. -- -- Note that all names in a JoinTable should be unquoted. data JoinTable a b JoinTable :: !ByteString -> !ByteString -> !ByteString -> JoinTable a b -- | Name of the join table in the database. (Not quoted.) jtTable :: JoinTable a b -> !ByteString -- | Name of the column in table jtTable that contains a -- DBRef to model a. (Not quoted or table-qualified.) jtColumnA :: JoinTable a b -> !ByteString -- | Like jtColumnA for model b. jtColumnB :: JoinTable a b -> !ByteString -- | The default join table has the following fields: -- --
-- data ParentBar = ParentBar -- instance RowAlias ParentBar where rowAliasName _ = "parent_bar" -- -- selfJoinTable :: JoinTable Bar (As ParentBar Bar) -- selfJoinTable = defaultJoinTable -- -- selfJoin :: Association Bar (As ParentBar Bar) -- otherSelfJoin :: Association (As ParentBar Bar) Bar -- (selfJoin, otherSelfJoin) = jtAssocs selfJoinTable --defaultJoinTable :: (Model a, Model b) => JoinTable a b -- | Generate the two associations implied by a JoinTable. jtAssocs :: (Model a, Model b) => JoinTable a b -> (Association a b, Association b a) -- | Generate a one-way association based on the default join table naming -- scheme described at defaultJoinTable. Defined as: -- --
-- joinTable = jtAssoc defaultJoinTable ---- -- For example: -- --
-- aToB :: Association A B -- aToB = joinTable -- -- bToA :: Association B A -- bToA = joinTable --joinTable :: (Model a, Model b) => Association a b -- | Add an association between two models to a join table. Returns -- True if the association was not already there. jtAdd :: (Model a, Model b) => JoinTable a b -> Connection -> a -> b -> IO Bool -- | Remove an association from a join table. Returns True if the -- association was previously there. jtRemove :: (Model a, Model b) => JoinTable a b -> Connection -> a -> b -> IO Bool -- | Remove an assocation from a join table when you don't have the target -- instances of the two models handy, but do have references. jtRemoveByRef :: (Model a, Model b) => JoinTable a b -> Connection -> GDBRef rt a -> GDBRef rt b -> IO Bool -- | A SQL statement suitable for adding a pair to a join table. Note that -- the statement takes two parameters (i.e., contains two '?' -- characters) corresponding to the primary keys of the two models being -- associated. These parameters can be supplied by jtParam. jtAddStatement :: JoinTable a b -> Query -- | A SQL statement for removing a pair from a join table. Like -- jtAddStatement, the query is parameterized by two primary keys. jtRemoveStatement :: JoinTable a b -> Query -- | Generate parameters for jtAddStatement and -- jtRemoveStatement. The returned list is suitable for use as a -- ToRow instance. For example: -- --
-- execute conn (jtAddStatement my_join_table) (jtParam a b) --jtParam :: (Model a, Model b) => JoinTable a b -> a -> b -> [Action] -- | Flip a join table. This doesn't change the name of the table (since -- the same join table is used in both directions, and the default join -- table name glues together the two model names in alphabetical order -- anyway). jtFlip :: JoinTable a b -> JoinTable b a -- | Generate a one-way association from a JoinTable. Use -- jtAssocs instead. jtAssoc :: (Model a, Model b) => JoinTable a b -> Association a b -- | Combine two associations into one. nestAssoc :: (Model a, Model b) => Association a b -> Association b c -> Association a (b :. c) -- | Combine two associations into one, and project away the middle type. -- (The middle type can still be mentioned in WHERE clauses.) -- -- An example: -- --
-- data Author = Author {
-- authorId :: DBKey
-- } deriving (Show, Generic)
-- instance Model Author where modelInfo = underscoreModelInfo "author"
--
-- data Post = Post {
-- postId :: DBKey
-- , postAuthorId :: DBRef Author
-- } deriving (Show, Generic)
-- instance Model Post where modelInfo = underscoreModelInfo "post"
--
-- data Comment = Comment {
-- commentId :: DBKey
-- , commentPostId :: DBRef Post
-- } deriving (Show, Generic)
-- instance Model Comment where modelInfo = underscoreModelInfo "comment"
--
-- author_posts :: Association Author Post
-- post_author :: Association Post Author
-- (post_author, author_posts) = dbrefAssocs defaultDBRefInfo
--
-- -- Could equally well use dbrefAssocs as above
-- post_comments :: Association Post Comment
-- post_comments = has
--
-- comment_post :: Association Comment Post
-- comment_post = belongsTo
--
-- comment_author :: Association Comment Author
-- comment_author = chainAssoc comment_post post_author
--
-- author_comments :: Association Author Comment
-- author_comments = chainAssoc author_posts post_comments
--
chainAssoc :: (Model a, Model b, Model c) => Association a b -> Association b c -> Association a c
instance Show (ExtractRef a)
instance Show (JoinTable a b)
instance Extractor ExtractRef (Maybe (GDBRef rt a)) (DBRef (As alias a)) THasOne
instance Extractor ExtractRef (Maybe (GDBRef rt a)) (DBRef a) THasOne
instance Extractor ExtractRef (GDBRef rt a) (DBRef (As alias a)) THasOne
instance Extractor ExtractRef (GDBRef rt a) (DBRef a) THasOne
instance Show (GDBRefInfo rt c p)
instance Show (Association a b)
module Database.PostgreSQL.ORM
-- | The class of data types that represent a database table. This class
-- conveys information necessary to move a Haskell data structure in and
-- out of a database table. The most important field is modelInfo,
-- which describes the database table and column names. modelInfo
-- has a reasonable default implementation for types that are members of
-- the Generic class (using GHC's DeriveGeneric
-- extension), provided the following conditions hold:
--
--
-- data MyType = MyType { myKey :: !DBKey
-- , myName :: !S.ByteString
-- , myCamelCase :: !Int
-- , ...
-- } deriving (Show, Generic)
-- instance Model MyType
--
--
-- The default modelInfo method is called defaultModelInfo.
-- You may wish to use almost all of the defaults, but tweak a few
-- things. This is easily accomplished by overriding a few fields of the
-- default structure. For example, suppose your database columns use
-- exactly the same name as your Haskell field names, but the name of
-- your database table is not the same as the name of the Haskell data
-- type. You can override the database table name (field
-- modelTable) as follows:
--
--
-- instance Model MyType where
-- modelInfo = defaultModelInfo { modelTable = "my_type" }
--
--
-- Finally, if you dislike the conventions followed by
-- defaultModelInfo, you can simply implement an alternate
-- pattern. An example of this is underscoreModelInfo, which
-- strips a prefix off every field name and converts everything from
-- camel-case to underscore notation:
--
-- -- instance Model MyType where -- modelInfo = underscoreModelInfo "my" ---- -- The above code will associate MyType with a database table -- my_type having column names key, name, -- camel_case, etc. -- -- You can implement other patterns like underscoreModelInfo by -- calling defaultModelInfo and modifying the results. -- Alternatively, you can directly call the lower-level functions from -- which defaultModelInfo is built (defaultModelTable, -- defaultModelColumns, defaultModelGetPrimaryKey). class Model a where modelInfo = defaultModelInfo modelIdentifiers = defaultModelIdentifiers modelInfo modelRead = defaultFromRow modelWrite = defaultModelWrite modelQueries = defaultModelQueries modelIdentifiers modelCreateInfo = emptyModelCreateInfo modelValid = const [] modelInfo :: Model a => ModelInfo a modelValid :: Model a => a -> [InvalidError] -- | A ModelInfo T contains the information necessary for mapping -- T to a database table. Each Model type has a -- single ModelInfo associated with it, accessible through the -- modelInfo method of the Model class. Note the table and -- column names must all be unquoted in this data structure, as they will -- later be quoted using quoteIdent by the modelIdentifiers -- method. data ModelInfo a ModelInfo :: !ByteString -> ![ByteString] -> !Int -> !(a -> DBKey) -> ModelInfo a -- | The name of the database table corresponding to this model. The -- default modelInfo instance uses defaultModelTable, which -- is the name of your data type with the first letter downcased. modelTable :: ModelInfo a -> !ByteString -- | The names of the database columns corresponding to fields of this -- model. The column names should appear in the order in which the fields -- are defined in the Haskell data type a (which should also be -- the order in which modelRead parses them to an a and -- modelWrite marshalls them). -- -- Note that all queries generated by the library specify explicit column -- names. Hence the order of columns does not need to match their order -- in the database table. They should instead match the order of fields -- in the Haskell data structure. -- -- The default, given by defaultModelColumns, is to use the -- Haskell field names for a. This default will fail to compile -- if a is not defined using record syntax. modelColumns :: ModelInfo a -> ![ByteString] -- | The 0-based index of the primary key column in modelColumns. -- This should be 0 when your data structure's first field is its -- DBKey (highly recommended, and required by -- defaultModelGetPrimaryKey). If you customize this field, you -- must also customize modelGetPrimaryKey--no check is made that -- the two are consistent. modelPrimaryColumn :: ModelInfo a -> !Int -- | Return the primary key of a particular model instance. If you -- customize this field, you must also customize -- modelPrimaryColumn--no check is made that the two are -- consistent. modelGetPrimaryKey :: ModelInfo a -> !(a -> DBKey) -- | The default definition of modelInfo. See the documentation at -- Model for more information. Sets modelTable to the name -- of the type with the first character converted to lower-case. Sets -- modelColumns to the names of the Haskell field selectors. Sets -- modelPrimaryColumn to 0 and extracts the first field -- of the structure for modelGetPrimaryKey. Will fail to compile -- unless the data structure is defined with record syntax and that its -- first field is of type DBKey. -- -- Note that defaults for the individual fields are available in separate -- functions (e.g., defaultModelTable) with fewer class -- requirements in the context, in case you want to make piecemeal use of -- defaults. The default for modelPrimaryColumn is 0. If you -- overwrite that, you will need to overwrite modelGetPrimaryKey -- as well (and likely vice versa). defaultModelInfo :: (Generic a, GDatatypeName (Rep a), GColumns (Rep a), GPrimaryKey0 (Rep a)) => ModelInfo a -- | An alternate Model pattern in which Haskell type and field -- names are converted from camel-case to underscore notation. The first -- argument is a prefix to be removed from field names (since Haskell -- requires field names to be unique across data types, while SQL allows -- the same column names to be used in different tables). -- -- For example: -- --
-- data Bar = Bar {
-- barId :: !DBKey
-- , barNameOfBar :: !String
-- , barParent :: !(Maybe (DBRef Bar))
-- } deriving (Show, Generic)
--
-- instance Model Bar where modelInfo = underscoreModelInfo "bar"
--
--
-- would associate type Bar with a database table called
-- bar with fields id, name_of_bar, and
-- parent.
underscoreModelInfo :: (Generic a, GToRow (Rep a), GFromRow (Rep a), GPrimaryKey0 (Rep a), GColumns (Rep a), GDatatypeName (Rep a)) => ByteString -> ModelInfo a
-- | The type of the Haskell data structure field containing a model's
-- primary key.
--
-- Every Model must have exactly one DBKey, and the
-- DBKey must be the Model's very first field in the
-- Haskel data type definition. (The ordering is enforced by
-- defaultModelGetPrimaryKey, which, through use of the
-- DeriveGeneric extension, fails to compile when the first
-- field is not a DBKey.)
--
-- Each Model stored in the database should have a unique non-null
-- primary key. However, the key is determined at the time the
-- Model is inserted into the database. While you are constructing
-- a new Model to insert, you will not have its key. Hence, you
-- should use the value NullKey to let the database chose the
-- key.
--
-- If you wish to store a Model's primary key as a reference in
-- another Model, do not copy the DBKey structure. Use
-- mkDBRef to convert the Model's primary key to a foreign
-- key reference.
data DBKey
DBKey :: !DBKeyType -> DBKey
NullKey :: DBKey
-- | A DBRef T represents a many-to-one relationship between
-- tables. For example, if type A contains a DBRef B,
-- then each B is associated with many A's. By
-- contrast, a DBRefUnique represents a one-to-one
-- relationship.
--
-- DBRef is a type alias of kind * -> *. The type
-- DBRef T references an instance of type T by the
-- primary key of its database row. The type argument T should
-- be an instance of Model.
type DBRef = GDBRef NormalRef
-- | A DBRefUnique T represents a one-to-one relationship between
-- types. For example, if type A contains a DBRefUnique
-- B, then each A is associated with one (or at most one)
-- B, and each B has one (or at most one) A
-- associated with it.
--
-- By contrast, a DBRef represents a many-to-one
-- relationship.
type DBRefUnique = GDBRef UniqueRef
-- | Create a reference to the primary key of a Model, suitable for
-- storing in a DBRef or DBRefUnique field of a different
-- Model.
mkDBRef :: Model a => a -> GDBRef rt a
-- | Lookup the primary key of a Model.
primaryKey :: Model a => a -> DBKey
-- | A composite type to parse your custom data structures without having
-- to define dummy newtype wrappers every time.
--
-- -- instance FromRow MyData where ... ---- --
-- instance FromRow MyData2 where ... ---- -- then I can do the following for free: -- --
-- res <- query' c ...
-- forM res $ \(MyData{..} :. MyData2{..}) -> do
-- ....
--
data (:.) h t :: * -> * -> *
-- | The newtype As can be wrapped around an existing type to give
-- it a table name alias in a query. This is necessary when a model is
-- being joined with itself, to distinguish the two joined instances of
-- the same table.
--
-- For example:
--
--
-- {-# LANGUAGE OverloadedStrings #-}
--
-- data X = X
-- instance RowAlias X where rowAliasName = const "x"
--
-- ...
-- r <- dbSelect c $ addWhere_ "bar.bar_key = x.bar_parent" modelDBSelect
-- :: IO [Bar :. As X Bar]
--
newtype As alias row
As :: row -> As alias row
unAs :: As alias row -> row
-- | The class of types that can be used as tags in as As alias.
-- Such types should be unit types--in other words, have exactly one
-- constructor where the constructor is nullary (take no arguments). The
-- reason for this class is that the Model instance for As
-- requires a way to extract the name of the row alias without having a
-- concrete instance of the type. This is provided by the
-- rowAliasName method (which must be non-strict).
class RowAlias a where rowAliasName _ = fromString $ caseFold $ gUnitTypeName . from $ a where caseFold (h : t) = toLower h : t caseFold [] = [] a = undefined :: a
rowAliasName :: RowAlias a => g a row -> ByteString
-- | fromAs extracts the row from an As alias
-- row, but constrains the type of alias to be the same as
-- its first argument (which is non-strict). This can save you from
-- explicitly specifying types. For example:
--
-- -- data X = X deriving (Generic) -- instance RowAlias X where rowAliasName = const "x" -- -- ... -- r <- map (\(b1 :. b2) -> (b1, fromAs X b2)) <$> -- dbSelect c $ addWhere \"bar.bar_key = x.bar_parent\" modelDBSelect --fromAs :: alias -> As alias row -> row -- | Follow a DBRef or DBRefUnique and fetch the target row -- from the database into a Model type r. findRow :: Model r => Connection -> GDBRef rt r -> IO (Maybe r) -- | Dump an entire model. Useful for development and debugging only, as -- every row will be read into memory before the function returns. -- -- Note that unlike the other primary model operations, it is OK to call -- findAll even on degenerate models such as As and -- :.. findAll :: Model r => Connection -> IO [r] -- | Like trySave but instead of returning an Either, throws -- a ValidationError if the Model is invalid. save :: Model r => Connection -> r -> IO r -- | Write a Model to the database. If the primary key is -- NullKey, the item is written with an INSERT query, -- read back from the database, and returned with its primary key filled -- in. If the primary key is not NullKey, then the Model is -- written with an UPDATE query and returned as-is. -- -- If the Model is invalid (i.e. the return value of -- modelValid is non-empty), a list of InvalidError is -- returned instead. trySave :: Model r => Connection -> r -> IO (Either [InvalidError] r) -- | Remove the row corresponding to a particular data structure from the -- database. This function only looks at the primary key in the data -- structure. It is an error to call this function if the primary key is -- not set. destroy :: Model a => Connection -> a -> IO () -- | Remove a row from the database without fetching it first. destroyByRef :: Model a => Connection -> GDBRef rt a -> IO () -- | A deconstructed SQL select statement that allows easier manipulation -- of individual terms. Several functions are provided to combine the -- selFields, selFrom, and selWhere clauses of -- muliple DBSelect structures. Other clauses may be discarded -- when combining queries with join operations. Hence it is advisable to -- set the other clauses at the end (or, if you set these fields, to -- collapse your DBSelect structure into a subquery using -- dbProject'). data DBSelect a DBSelect :: !Query -> !Query -> Query -> !FromClause -> !Query -> !Query -> !Query -> !Query -> !Query -> !Query -> !Query -> DBSelect a selWith :: DBSelect a -> !Query -- | By default "SELECT", but might usefully be set to something -- else such as "SELECT DISTINCT" in some situations. selSelectKeyword :: DBSelect a -> !Query selFields :: DBSelect a -> Query selFrom :: DBSelect a -> !FromClause -- | Empty by default, but set to "WHERE" if any WHERE -- clauses are added to the selWhere field. selWhereKeyword :: DBSelect a -> !Query selWhere :: DBSelect a -> !Query selGroupBy :: DBSelect a -> !Query selHaving :: DBSelect a -> !Query selOrderBy :: DBSelect a -> !Query selLimit :: DBSelect a -> !Query selOffset :: DBSelect a -> !Query -- | A DBSelect that returns all rows of a model. modelDBSelect :: Model a => DBSelect a -- | Run a DBSelect query on parameters. The number of '?' -- characters embedeed in various fields of the DBSelect must -- exactly match the number of fields in parameter type p. Note -- the order of arguments is such that the DBSelect can be -- pre-rendered and the parameters supplied later. Hence, you should use -- this version when the DBSelect is static. For dynamically -- modified DBSelect structures, you may prefer dbSelect. dbSelectParams :: (Model a, ToRow p) => DBSelect a -> Connection -> p -> IO [a] -- | Run a DBSelect query and return the resulting models. dbSelect :: Model a => Connection -> DBSelect a -> IO [a] -- | Add a where clause verbatim to a DBSelect. The clause must -- not contain the WHERE keyword (which is added -- automatically by addWhere_ if needed). If the -- DBSelect has existing WHERE clauses, the new clause -- is appended with AND. If the query contains any '?' -- characters, they will be rendered into the query and matching -- parameters will later have to be filled in via a call to -- dbSelectParams. addWhere_ :: Query -> DBSelect a -> DBSelect a -- | Add a where clause, and pre-render parameters directly into the -- clause. The argument p must have exactly as many fields as -- there are '?' characters in the Query. Example: -- --
-- bars <- dbSelect c $ addWhere "bar_id = ?" (Only target_id) $ -- (modelDBSelect :: DBSelect Bar) --addWhere :: ToRow p => Query -> p -> DBSelect a -> DBSelect a -- | Set the ORDER BY clause of a DBSelect. Example: -- --
-- dbSelect c $ setOrderBy "\"employeeName\" DESC NULLS FIRST" $ -- modelDBSelect --setOrderBy :: Query -> DBSelect a -> DBSelect a -- | Set the LIMIT clause of a DBSelect. setLimit :: Int -> DBSelect a -> DBSelect a -- | Set the OFFSET clause of a DBSelect. setOffset :: Int -> DBSelect a -> DBSelect a -- | A data structure representing a relationship between a model -- a and a model b. At a high level, an Association -- a b tells you how to find rows of type b given rows of -- type a. More concretely, this boils down to being able to -- make two types of query. -- --
-- findAssoc' ab c a = dbSelect c $ assocWhere ab a ---- -- But if the first argument is a static association, this function may -- be marginally faster because it pre-renders most of the query. findAssoc :: Model b => Association a b -> Connection -> a -> IO [b] -- | A common type of association is when one model contains a DBRef -- or DBRefUnique pointing to another model. In this case, the -- model containing the DBRef is known as the child, and -- the referenced model is known as the parent. -- -- Two pieces of information are required to describe a parent-child -- relationship: First, the field selector that extracts the Haskell -- DBRef from the haskell type child, and second the name -- of the database column that stores this DBRef field. -- -- For example, consider the following: -- --
-- data Author = Author {
-- authorId :: DBKey
-- } deriving (Show, Generic)
-- instance Model Author
--
-- data Post = Post {
-- postId :: DBKey
-- , postAuthorId :: DBRef Author
-- } deriving (Show, Generic)
-- instance Model Post
--
-- post_author_refinfo :: DBRefInfo Post Author
-- post_author_refinfo = DBRefInfo {
-- dbrefSelector = postAuthorId
-- , dbrefQColumn = "\"post\".\"postAuthorId\""
-- }
--
--
-- Note that the parent-child relationship described by a
-- GDBRefInfo is asymmetric, but bidirectional. When a
-- DBRefInfo child parent exists, the schema should
-- generally not permit the existence of a valid
-- DBRefInfo parent child structure. However, the
-- dbrefAssocs function generates Associations in both
-- directions from a single DBRefInfo.
--
-- Constructing such parent-child Associations requires knowing
-- how to extract primary keys from the parent type as well as
-- the name of the column storing primary keys in parent.
-- Fortunately, this information is already available from the
-- Model class, and thus does not need to be in the
-- GDBRefInfo. (Most functions on GDBRefInfos require
-- parent and child to be instances of Model.)
--
-- When your Models are instances of Generic (which will
-- usually be the case), a DBRefInfo structure can be computed
-- automatically by defaultDBRefInfo. This is the recommended way
-- to produce a GDBRefInfo. (Alternatively, see has and
-- belongsTo to make use of an entirely implicit
-- DBRefInfo.)
data GDBRefInfo reftype child parent
DBRefInfo :: !(child -> GDBRef reftype parent) -> !ByteString -> GDBRefInfo reftype child parent
-- | Field selector returning a reference.
dbrefSelector :: GDBRefInfo reftype child parent -> !(child -> GDBRef reftype parent)
-- | Literal SQL for the database column storing the reference. This should
-- be double-quoted and table-qualified, in case the column name is a
-- reserved keyword, contains capital letters, or conflicts with the name
-- of a column in the joined table. An example would be:
--
-- -- dbrefQColumn = "\"table_name\".\"column_name\"" --dbrefQColumn :: GDBRefInfo reftype child parent -> !ByteString -- | DBRefInfo is a type alias for the common case that the -- reference in a GDBRefInfo is a DBRef (as opposed to a -- DBRefUnique). The functions in this library do not care what -- type of reference is used. The type is generalized to -- GDBRefInfo just to make it easier to assign a selector to -- dbrefSelector when the selector returns a DBRefUnique. -- Note, however, that defaultDBRefInfo returns a DBRefInfo -- regardless of the flavor of reference actually encountered. type DBRefInfo = GDBRefInfo NormalRef -- | Creates a DBRefInfo from a model child that references -- parent. For this to work, the child type must be an -- instance of Generic and must contain exactly one field of the -- any of the following types: -- --
-- data Bar = Bar {
-- barId :: !DBKey
-- , barName :: !String
-- , barParent :: !(Maybe (DBRef Bar))
-- } deriving (Show, Generic)
-- instance Model Bar where modelInfo = underscoreModelInfo "bar"
--
-- data ParentBar = ParentBar
-- instance RowAlias ParentBar where rowAliasName _ = "parent_bar"
--
-- toParent :: Association Bar (As ParentBar Bar)
-- toParent = belongsTo
--
-- toChild :: Association (As ParentBar Bar) Bar
-- toChild = has
--
defaultDBRefInfo :: (Model child, Model parent, GetField ExtractRef child (DBRef parent)) => DBRefInfo child parent
-- | Generate both the child-parent and parent-child Associations
-- implied by a GDBRefInfo.
dbrefAssocs :: (Model child, Model parent) => GDBRefInfo rt child parent -> (Association child parent, Association parent child)
-- | Short for
--
-- -- snd $ dbrefAssocs defaultDBRefInfo ---- -- Note the inverse Association is given by belongsTo. For -- example, given the Author and Post models described -- in the documentation for GDBRefInfo, in which each -- Post references an Author, you might say: -- --
-- author_post :: Association Author Post -- author_post = has -- -- post_author :: Association Post Author -- post_author = belongsTo --has :: (Model child, Model parent, GetField ExtractRef child (DBRef parent)) => Association parent child -- | The inverse of has. Short for -- --
-- fst $ dbrefAssocs defaultDBRefInfo ---- -- See an example at has. belongsTo :: (Model child, Model parent, GetField ExtractRef child (DBRef parent)) => Association child parent -- | A data structure representing a dedicated join table in the database. -- A join table differs from a model in that rows do not have primary -- keys. Hence, model operations do not apply. Nonetheless a join table -- conveys information about a relationship between models. -- -- Note that all names in a JoinTable should be unquoted. data JoinTable a b JoinTable :: !ByteString -> !ByteString -> !ByteString -> JoinTable a b -- | Name of the join table in the database. (Not quoted.) jtTable :: JoinTable a b -> !ByteString -- | Name of the column in table jtTable that contains a -- DBRef to model a. (Not quoted or table-qualified.) jtColumnA :: JoinTable a b -> !ByteString -- | Like jtColumnA for model b. jtColumnB :: JoinTable a b -> !ByteString -- | The default join table has the following fields: -- --
-- data ParentBar = ParentBar -- instance RowAlias ParentBar where rowAliasName _ = "parent_bar" -- -- selfJoinTable :: JoinTable Bar (As ParentBar Bar) -- selfJoinTable = defaultJoinTable -- -- selfJoin :: Association Bar (As ParentBar Bar) -- otherSelfJoin :: Association (As ParentBar Bar) Bar -- (selfJoin, otherSelfJoin) = jtAssocs selfJoinTable --defaultJoinTable :: (Model a, Model b) => JoinTable a b -- | Generate the two associations implied by a JoinTable. jtAssocs :: (Model a, Model b) => JoinTable a b -> (Association a b, Association b a) -- | Add an association between two models to a join table. Returns -- True if the association was not already there. jtAdd :: (Model a, Model b) => JoinTable a b -> Connection -> a -> b -> IO Bool -- | Remove an association from a join table. Returns True if the -- association was previously there. jtRemove :: (Model a, Model b) => JoinTable a b -> Connection -> a -> b -> IO Bool -- | Remove an assocation from a join table when you don't have the target -- instances of the two models handy, but do have references. jtRemoveByRef :: (Model a, Model b) => JoinTable a b -> Connection -> GDBRef rt a -> GDBRef rt b -> IO Bool -- | Combine two associations into one. nestAssoc :: (Model a, Model b) => Association a b -> Association b c -> Association a (b :. c) -- | Combine two associations into one, and project away the middle type. -- (The middle type can still be mentioned in WHERE clauses.) -- -- An example: -- --
-- data Author = Author {
-- authorId :: DBKey
-- } deriving (Show, Generic)
-- instance Model Author where modelInfo = underscoreModelInfo "author"
--
-- data Post = Post {
-- postId :: DBKey
-- , postAuthorId :: DBRef Author
-- } deriving (Show, Generic)
-- instance Model Post where modelInfo = underscoreModelInfo "post"
--
-- data Comment = Comment {
-- commentId :: DBKey
-- , commentPostId :: DBRef Post
-- } deriving (Show, Generic)
-- instance Model Comment where modelInfo = underscoreModelInfo "comment"
--
-- author_posts :: Association Author Post
-- post_author :: Association Post Author
-- (post_author, author_posts) = dbrefAssocs defaultDBRefInfo
--
-- -- Could equally well use dbrefAssocs as above
-- post_comments :: Association Post Comment
-- post_comments = has
--
-- comment_post :: Association Comment Post
-- comment_post = belongsTo
--
-- comment_author :: Association Comment Author
-- comment_author = chainAssoc comment_post post_author
--
-- author_comments :: Association Author Comment
-- author_comments = chainAssoc author_posts post_comments
--
chainAssoc :: (Model a, Model b, Model c) => Association a b -> Association b c -> Association a c
data InvalidError
InvalidError :: !ByteString -> !ByteString -> InvalidError
invalidColumn :: InvalidError -> !ByteString
invalidError :: InvalidError -> !ByteString
newtype ValidationError
ValidationError :: [InvalidError] -> ValidationError
validate :: (a -> Bool) -> ByteString -> ByteString -> ValidationFunc a
validateNotEmpty :: (a -> Text) -> ByteString -> ByteString -> ValidationFunc a
-- | Functions for creating a table from a model. These are mostly useful
-- in development, for very rigid applications, or to compare what would
-- be created against what is actually in the database. In practice,
-- production settings should create and update tables using migrations.
--
-- Note that often it is more interesting to see what would be created
-- than to create an actual table. For that reason, functions creating
-- the statements are exposed.
module Database.PostgreSQL.ORM.CreateTable
-- | Statement for creating the table corresponding to a model. Not strict
-- in its argument.
modelCreateStatement :: (Model a, Generic a, GDefTypes (Rep a)) => a -> Query
-- | Create a the database table for a model.
modelCreate :: (Model a, Generic a, GDefTypes (Rep a)) => Connection -> a -> IO Int64
-- | This is a helper class used to extract the row types. You don't need
-- to use this class. If you are creating custom types, just declare an
-- instance of SqlType.
class GDefTypes f
gDefTypes :: GDefTypes f => f p -> [ByteString]
-- | Create the database table corresponding to a JoinTable.
jtCreateStatement :: (Model a, Model b) => JoinTable a b -> Query
-- | Create a join table in the database.
jtCreate :: (Model a, Model b) => Connection -> JoinTable a b -> IO Int64
instance GDefTypes f => GDefTypes (M1 i c f)
instance (GDefTypes a, GDefTypes b) => GDefTypes (a :*: b)
instance SqlType c => GDefTypes (K1 i c)