packages feed

pg-schema-0.8.0.0: src/PgSchema/DML/KeyedWrite.hs

{-# LANGUAGE OverloadedRecordDot #-}
module PgSchema.DML.KeyedWrite
  ( mandatoryDbNames, identityCandidatesFromTab, pickKeyNames
  , keyWhereClause, upsertOnConflict
  , keyedUpdateSetAndKeys ) where

import Data.List as L
import Data.Map as M
import Data.Maybe
import Data.Text as T
import PgSchema.Schema
import Prelude as P


-- | Runtime conflict/update targets (same order as 'InsertJSON').
identityCandidatesFromTab :: TabInfo -> [[Text]]
identityCandidatesFromTab ti =
  P.filter (not . P.null) [ti.tiDef.tdKey] <> notNullUks <> nullUks
  where
    isNullable = (== Just True) . fmap (.fdNullable) . (`M.lookup` ti.tiFlds)
    (nullUks, notNullUks) = L.partition (P.any isNullable) ti.tiDef.tdUniq

mandatoryDbNames :: TabInfo -> [Text]
mandatoryDbNames ti =
  M.keys $ M.filter (\fd -> not $ fd.fdNullable || fd.fdHasDefault) ti.tiFlds

pickKeyNames :: TabInfo -> [Text] -> Maybe [Text]
pickKeyNames ti srcFlds =
  L.find (P.null . (L.\\ srcFlds)) $ identityCandidatesFromTab ti

keyWhereClause :: [(Text, Text)] -> Text
keyWhereClause pairs =
  " where " <> T.intercalate " and "
    (L.zipWith keyValDistinct (fst <$> pairs) (snd <$> pairs))
  where
    keyValDistinct name val = name <> " IS NOT DISTINCT FROM " <> val

-- | Partition plain columns for @UPDATE@ (SET non-keys, then WHERE keys).
keyedUpdateSetAndKeys
  :: TabInfo -> [(Text, Text)] -> ([(Text, Text)], [(Text, Text)])
keyedUpdateSetAndKeys ti plains =
  let
    srcFlds = fst <$> plains
    keyNames = fromMaybe [] $ pickKeyNames ti srcFlds
  in L.partition ((`L.elem` keyNames) . fst) plains

upsertOnConflict :: [(Text, Text)] -> [Text] -> Text
upsertOnConflict plainsOthers conflictCols =
  case (L.null plainsOthers, conflictCols) of
    (True, _) -> " on conflict do nothing"
    (_, []) -> ""
    (_, cols) ->
      " on conflict (" <> T.intercalate ", " cols <> ")"
        <> " do update set "
        <> T.intercalate ", "
          ( L.map (\(name, _) -> name <> " = EXCLUDED." <> name) plainsOthers )