pg-schema-0.5.0.0: src/PgSchema/DML.hs
-- |
-- Module: PgSchema.DML
-- Copyright: (c) Dmitry Olshansky
-- License: BSD-3-Clause
-- Maintainer: olshanskydr@gmail.com, dima@typeable.io
-- Stability: experimental
--
-- = A module to generate and safely execute SQL statements.
--
-- Among @pg-schema@’s library modules, this is the one meant for ordinary application
-- imports for database access (alongside your generated schema module).
--
-- == TLDR; Example:
--
-- @
-- data Order = Order { num :: Text, createdAt :: Day, items :: [OrdPos] } deriving Generic
-- data OrdPos = OrdPos { id :: Int32, num :: Int32, article :: Article, price :: Double } deriving Generic
-- data Article = Article { id :: Int32, name :: Text } deriving Generic
-- type MyAnn tabName = 'Ann 5 CamelToSnake MySch ("dbSchema" ->> tabName)
-- ...
-- do
-- void $ insertJSON_ (MyAnn "orders") conn
-- [ "num" =: "num1" :. "ord__ord_pos" =:
-- [ "num" =: 1 :. "articleId" =: 42 :. "price" =: 10
-- , "num" =: 2 :. "articleId" =: 41 :. "price" =: 15 ] ]
--
-- (xs :: [Order]) <- selectSch (MyAnn "orders") conn $ qRoot do
-- qWhere $ "created_at" >? someDay
-- &&& pchild "ord__ord_pos" defTabParam
-- (pparent "ord_pos__article" $ "name" ~~? "%pencil%")
-- qOrderBy [descf "created_at", descf "num"]
-- qPath "ord__ord_pos" do
-- qWhere $ pparent "ord_pos__article" $ "name" ~~? "%pencil%"
-- qOrderBy [ascf "num"]
-- qLimit 20
-- @
--
-- Here we get all orders created after `someDay` that have positions with articles like "%pencil%",
-- and return only those order items that relate to "pencil", with article info.
--
-- The preceding 'insertJSON_' call inserts one root row (here an order) and nested
-- /child/ rows (here order positions) in a single database operation, again using
-- JSON internally. Child data is carried in list fields: the field’s name (after 'Renamer') names
-- the FK constraint in the database and thus selects the child table and link;
-- each list element supplies one child row’s columns, with nested lists for further
-- children in the same way. For strict inserts,
-- 'insertJSON' and 'insertJSON_' require every mandatory column at each node;
-- 'upsertJSON' / 'upsertJSON_' relax that so each row can be resolved by keys and
-- optional columns (see their Haddock). Plain 'insertSch' / 'insertSch_' follow the
-- same safety story for flat (non-tree) rows.
--
-- 'selectSch' decodes each row into a Haskell type @r@ whose fields describe the
-- root table columns and nested data for relations (multiple list-shaped children
-- and nested parents/'Maybe' as appropriate). The type drives the generated SQL
-- shape; monadic 'QueryParam' only adds what this call needs—filters, ordering on a
-- branch, limits, and so on.
--
-- We can use both 'GHC.Generics.Generic'-based and 'PgTag'-based ('(=:)') interfaces and mix them in any way.
--
-- All string literals at the type level are 'GHC.TypeLits.Symbol' thanks to @RequiredTypeArguments@.
-- Operations are safe when the live database schema matches the generated schema types.
--
-- The @INSERT@ and @SELECT@ shown here each run as a single, fast database round-trip (JSON inside PostgreSQL).
--
-- We use a 'Renamer' (e.g. 'CamelToSnake') to map Haskell field names to @snake_case@ in the database.
-- In conditions and 'QueryParams', column names are the original database names.
-- It can be changed in the future.
--
module PgSchema.DML
(
-- * DML
-- ** Select
-- *** Execute SQL
selectSch, selectText, Selectable
-- *** Monad to set Query Params
, MonadQP, qpEmpty
, qRoot, qPath, qWhere, qOrderBy, qDistinct, qDistinctOn, qLimit, qOffset
-- **** Internals
, QueryParam(..), CondWithPath(..), OrdWithPath(..), LimOffWithPath(..), DistWithPath(..)
-- *** Conditions
-- | Example: @"name" =? "John"@
, (<?),(>?),(<=?),(>=?),(=?)
-- | Example: @"name" ~~? "%joh%"@
,(~=?),(~~?)
, (|||), (&&&), pnot, pnull, pin, pinArr
, pparent, pchild, TabParam(..), defTabParam
, pUnsafeCond
, Cond(..), Cmp(..), BoolOp(..)
, CondMonad, SomeToField(..), showCmp, tabPref, qual
, CDBField, CDBValue, CDBFieldNullable, CRelDef
-- *** Order By and others
, ordf, ascf, descf, defLO, lo1
, OrdDirection(..), OrdFld(..), Dist(..), LO(..)
-- ** Plain Insert
, insertSch, insertSch_, insertText, insertText_, AllPlain
, InsertNonReturning, InsertReturning
-- ** Update
, updateByCond, updateByCond_, updateText, updateText_
, UpdateReturning, UpdateNonReturning, CRecInfo(..)
-- ** Tree-base Insert/Upsert
, insertJSON, insertJSON_, upsertJSON, upsertJSON_, insertJSONText, insertJSONText_
, TreeIn, TreeOut, AllMandatoryTree, AllMandatoryOrHasPKTree, TreeSch
, InsertTreeNonReturning, InsertTreeReturning
, UpsertTreeNonReturning, UpsertTreeReturning, TRecordInfo
-- ** Delete
, deleteByCond, deleteText
-- * Types
, Ann(..), ToStar
-- ** Renamers
, RenamerId, CamelToSnake, Renamer, ApplyRenamer
-- ** PgTag types
, type (:=), (=:), PgTag(..),
-- | Re-export from postgresql-simple
(:.)(..)
-- ** Enum
, PGEnum
-- ** Aggregates
, Aggr'(..), Aggr(..), AggrFun(..)
-- ** Arrays
, PgArr(..), pgArr', unPgArr'
-- ** Conversion checks
, CanConvert, CanConvert1, TypDef'(..)
-- ** Other types
, PgChar(..), PgOid(..)
, NameNS'(..), type (->>), NameNSK, (->>)
, TRelDef, RelDef'(..), RdFrom, RdTo, FdType, FdNullable, CTabDef(..)
) where
import PgSchema.Ann
import PgSchema.DML.Select as S
import PgSchema.DML.Select.Types as S
import PgSchema.DML.Insert as I
import PgSchema.DML.Insert.Types as I
import PgSchema.DML.InsertJSON as I
import PgSchema.DML.Update as U
import PgSchema.DML.Delete as D
import PgSchema.Schema as S
import PgSchema.Types as T
import PgSchema.Utils.CamelToSnake
import PgSchema.Utils.Internal as T (ToStar)