packages feed

hpqtypes-extras 1.13.1.0 → 1.14.0.0

raw patch · 4 files changed

+140/−577 lines, 4 filesdep ~base16-bytestringdep ~bytestringdep ~containersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base16-bytestring, bytestring, containers, cryptohash, exceptions, extra, fields-json, hpqtypes, lifted-base, log-base, monad-control, mtl, safe, semigroups, text, text-show

API changes (from Hackage documentation)

- Database.PostgreSQL.PQTypes.SQL.Builder: DBBaseLineConditionIsFalse :: SQL -> DBBaseLineConditionIsFalse
- Database.PostgreSQL.PQTypes.SQL.Builder: SqlWhyNot :: Bool -> (row -> e) -> [SQL] -> SqlWhyNot
- Database.PostgreSQL.PQTypes.SQL.Builder: class (SqlWhere a, Sqlable a) => SqlTurnIntoSelect a
- Database.PostgreSQL.PQTypes.SQL.Builder: data SqlWhyNot
- Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlTurnIntoSelect Database.PostgreSQL.PQTypes.SQL.Builder.SqlDelete
- Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlTurnIntoSelect Database.PostgreSQL.PQTypes.SQL.Builder.SqlInsertSelect
- Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlTurnIntoSelect Database.PostgreSQL.PQTypes.SQL.Builder.SqlSelect
- Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlTurnIntoSelect Database.PostgreSQL.PQTypes.SQL.Builder.SqlUpdate
- Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlWhere a => Database.PostgreSQL.PQTypes.SQL.Builder.SqlWhere (Database.PostgreSQL.PQTypes.SQL.Builder.SqlWhereIgnore a)
- Database.PostgreSQL.PQTypes.SQL.Builder: instance GHC.Exception.Type.Exception Database.PostgreSQL.PQTypes.SQL.Builder.DBBaseLineConditionIsFalse
- Database.PostgreSQL.PQTypes.SQL.Builder: instance GHC.Exception.Type.Exception Database.PostgreSQL.PQTypes.SQL.Builder.DBKwhyNotInternalError
- Database.PostgreSQL.PQTypes.SQL.Builder: instance GHC.Show.Show Database.PostgreSQL.PQTypes.SQL.Builder.DBBaseLineConditionIsFalse
- Database.PostgreSQL.PQTypes.SQL.Builder: instance GHC.Show.Show Database.PostgreSQL.PQTypes.SQL.Builder.DBKwhyNotInternalError
- Database.PostgreSQL.PQTypes.SQL.Builder: instance GHC.Show.Show Database.PostgreSQL.PQTypes.SQL.Builder.SqlWhyNot
- Database.PostgreSQL.PQTypes.SQL.Builder: kRun1OrThrowWhyNot :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m) => s -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: kRun1OrThrowWhyNotAllowIgnore :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m) => s -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: kRunAndFetch1OrThrowWhyNot :: (IsSQL s, FromRow row, MonadDB m, MonadThrow m, SqlTurnIntoSelect s) => (row -> a) -> s -> m a
- Database.PostgreSQL.PQTypes.SQL.Builder: kRunManyOrThrowWhyNot :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m) => s -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: kWhyNot1 :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m) => s -> m SomeException
- Database.PostgreSQL.PQTypes.SQL.Builder: kWhyNot1Ex :: forall m s. (SqlTurnIntoSelect s, MonadDB m, MonadThrow m) => s -> m (Bool, SomeException)
- Database.PostgreSQL.PQTypes.SQL.Builder: newtype DBBaseLineConditionIsFalse
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlIgnore :: MonadState s m => State (SqlWhereIgnore s) a -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlTurnIntoSelect :: SqlTurnIntoSelect a => a -> SqlSelect
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlTurnIntoWhyNotSelect :: SqlTurnIntoSelect a => a -> SqlSelect
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereAnyE :: (Exception e, MonadState v m, SqlWhere v) => e -> [State SqlAll ()] -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereE :: (MonadState v m, SqlWhere v, Exception e) => e -> SQL -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereEV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a) => (a -> e, SQL) -> SQL -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereEVV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a, FromSQL b) => (a -> b -> e, SQL, SQL) -> SQL -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereEVVV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a, FromSQL b, FromSQL c) => (a -> b -> c -> e, SQL, SQL, SQL) -> SQL -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereEVVVV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a, FromSQL b, FromSQL c, FromSQL d) => (a -> b -> c -> d -> e, SQL, SQL, SQL, SQL) -> SQL -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereEqE :: (MonadState v m, SqlWhere v, Exception e, Show a, FromSQL a, ToSQL a) => (a -> a -> e) -> SQL -> a -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereILikeE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a) => (a -> a -> e) -> SQL -> a -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereInE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a) => ([a] -> a -> e) -> SQL -> [a] -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereIsNULLE :: (MonadState v m, SqlWhere v, Exception e, FromSQL a) => (a -> e) -> SQL -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereLikeE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a) => (a -> a -> e) -> SQL -> a -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereNotEqE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a) => (a -> a -> e) -> SQL -> a -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlWhereNotInE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a) => ([a] -> a -> e) -> SQL -> [a] -> m ()
+ Database.PostgreSQL.PQTypes.SQL.Builder: [sqlInsertSelectOnConflict] :: SqlInsertSelect -> Maybe (SQL, Maybe SQL)
+ Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlOnConflict Database.PostgreSQL.PQTypes.SQL.Builder.SqlInsert
+ Database.PostgreSQL.PQTypes.SQL.Builder: instance Database.PostgreSQL.PQTypes.SQL.Builder.SqlOnConflict Database.PostgreSQL.PQTypes.SQL.Builder.SqlInsertSelect
- Database.PostgreSQL.PQTypes.SQL.Builder: SqlInsertSelect :: SQL -> Bool -> [(SQL, SQL)] -> [SQL] -> SQL -> [SqlCondition] -> [SQL] -> [SQL] -> [SQL] -> Integer -> Integer -> [(SQL, SQL)] -> SqlInsertSelect
+ Database.PostgreSQL.PQTypes.SQL.Builder: SqlInsertSelect :: SQL -> Maybe (SQL, Maybe SQL) -> Bool -> [(SQL, SQL)] -> [SQL] -> SQL -> [SqlCondition] -> [SQL] -> [SQL] -> [SQL] -> Integer -> Integer -> [(SQL, SQL)] -> SqlInsertSelect
- Database.PostgreSQL.PQTypes.SQL.Builder: SqlPlainCondition :: SQL -> SqlWhyNot -> SqlCondition
+ Database.PostgreSQL.PQTypes.SQL.Builder: SqlPlainCondition :: SQL -> SqlCondition
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlOnConflictDoNothing :: MonadState SqlInsert m => m ()
+ Database.PostgreSQL.PQTypes.SQL.Builder: sqlOnConflictDoNothing :: (MonadState v m, SqlOnConflict v) => m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlOnConflictOnColumns :: Sqlable sql => MonadState SqlInsert m => [SQL] -> sql -> m ()
+ Database.PostgreSQL.PQTypes.SQL.Builder: sqlOnConflictOnColumns :: (MonadState v m, SqlOnConflict v, Sqlable sql) => [SQL] -> sql -> m ()
- Database.PostgreSQL.PQTypes.SQL.Builder: sqlOnConflictOnColumnsDoNothing :: MonadState SqlInsert m => [SQL] -> m ()
+ Database.PostgreSQL.PQTypes.SQL.Builder: sqlOnConflictOnColumnsDoNothing :: (MonadState v m, SqlOnConflict v) => [SQL] -> m ()

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+# hpqtypes-extras-1.14.0.0 (2021-12-13)+* Add Support for ON CONFLICT to sqlInsertSelect.+* Remove upper bounds from library dependencies.+* Remove kRun* and kWhyNot functions.+ # hpqtypes-extras-1.13.1.0 (2021-11-26) * Add support for including columns in indexes. 
hpqtypes-extras.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.2 name:                hpqtypes-extras-version:             1.13.1.0+version:             1.14.0.0 synopsis:            Extra utilities for hpqtypes library description:         The following extras for hpqtypes library:                      .@@ -73,23 +73,23 @@                  , Database.PostgreSQL.PQTypes.Utils.NubList    build-depends: base              >= 4.9     && < 5-               , hpqtypes          >= 1.8.0.0 && < 2.0.0.0-               , base16-bytestring >= 0.1     && < 1.1-               , bytestring        >= 0.10    && < 0.12-               , containers        >= 0.5     && < 0.7-               , cryptohash        >= 0.11    && < 0.12-               , exceptions        >= 0.10    && < 0.11-               , extra             >= 1.6.17  && < 1.8.0-               , mtl               >= 2.2     && < 2.3-               , fields-json       >= 0.4     && < 0.5-               , text              >= 1.2     && < 1.3-               , lifted-base       >= 0.2     && < 0.3-               , monad-control     >= 1.0.3.1 && < 1.1-               , semigroups        >= 0.16    && < 0.20-               , text-show         >= 3.7     && < 4+               , hpqtypes          >= 1.8.0.0+               , base16-bytestring >= 0.1+               , bytestring        >= 0.10+               , containers        >= 0.5+               , cryptohash        >= 0.11+               , exceptions        >= 0.10+               , extra             >= 1.6.17+               , mtl               >= 2.2+               , fields-json       >= 0.4+               , text              >= 1.2+               , lifted-base       >= 0.2+               , monad-control     >= 1.0.3.1+               , semigroups        >= 0.16+               , text-show         >= 3.7                , transformers-base-               , log-base          >= 0.11    && < 0.12-               , safe              >= 0.3     && < 0.4+               , log-base          >= 0.11+               , safe              >= 0.3    default-language: Haskell2010   default-extensions:
src/Database/PostgreSQL/PQTypes/SQL/Builder.hs view
@@ -82,80 +82,26 @@ :} SQL " UPDATE document_tags SET value=<123>  WHERE (name = 'abc') " -Exception returning and 'kWhyNot' are a subsystem for querying why a-query did not provide expected results. For example:--> let query = sqlUpdate "documents" $ do->   sqlSet "deleted" True->   sqlWhereEq "documents.id" 12345->   sqlWhereEqE DocumentDeleteFlagMustBe "documents.deleted" False->   sqlWhereILikeE DocumentTitleMustContain "documents.title" "%important%"-> result <- kRun query--If the result is zero then no document was updated. We would like to-know what happened. In query we have three filtering clauses. One is a-baseline: the one mentioning @documents.id@. Baseline clauses define-what objects we are talking about. Other clauses are correctness-checks and may fail if status of on object is unexpected. Using-'kWhyNot' we can see what is wrong with an object:--> problems <- kWhyNot query--Now @problems@ should contain a list of issues with rows that could be-possibly be affected by weren't due to correctness clauses. For-example it may state:--> problems = [[ DocumentDeleteFlagMustBe { documentDeleteFlagMustBe = False->                                        , documentDeleteFlagReallyIs = True->                                        }->             , DocumentTitleMustContain { documentTitleMustContain = "%important%"->                                        , documentTitleReallyIs = "Some contract v2"->                                        }->             ]]--Note: problems is a nested array, for each object we get a list of-issues pertaining to that object. If that list is empty, then it means-that baseline conditions failed or there is no such object that-fullfills all conditions at the same time although there are some that-fullfill each one separatelly.--Note: @kWhyNot@ is currently disabled. Use 'kWhyNot1' instead, which-returns a single exception.- -}  -- TODO: clean this up, add more documentation.  module Database.PostgreSQL.PQTypes.SQL.Builder   ( sqlWhere-  , sqlWhereE-  , sqlWhereEV-  , sqlWhereEVV-  , sqlWhereEVVV-  , sqlWhereEVVVV   , sqlWhereEq-  , sqlWhereEqE   , sqlWhereEqSql   , sqlWhereNotEq-  , sqlWhereNotEqE   , sqlWhereIn   , sqlWhereInSql-  , sqlWhereInE   , sqlWhereNotIn   , sqlWhereNotInSql-  , sqlWhereNotInE   , sqlWhereExists   , sqlWhereNotExists   , sqlWhereLike-  , sqlWhereLikeE   , sqlWhereILike-  , sqlWhereILikeE   , sqlWhereIsNULL   , sqlWhereIsNotNULL-  , sqlWhereIsNULLE -  , sqlIgnore-   , sqlFrom   , sqlJoin   , sqlJoinOn@@ -182,10 +128,6 @@   , sqlWith   , sqlUnion -  , SqlTurnIntoSelect-  , sqlTurnIntoSelect-  , sqlTurnIntoWhyNotSelect-   , sqlSelect   , sqlSelect2   , SqlSelect(..)@@ -199,7 +141,6 @@   , SqlDelete(..)    , sqlWhereAny-  , sqlWhereAnyE    , SqlResult   , SqlSet@@ -210,21 +151,9 @@   , SqlOffsetLimit   , SqlDistinct -   , SqlCondition(..)   , sqlGetWhereConditions -  , SqlWhyNot(..)-  , DBBaseLineConditionIsFalse(..)--  , kWhyNot1-  , kWhyNot1Ex-  --, DBExceptionCouldNotParseValues(..)-  , kRun1OrThrowWhyNot-  , kRun1OrThrowWhyNotAllowIgnore-  , kRunManyOrThrowWhyNot-  , kRunAndFetch1OrThrowWhyNot-   , Sqlable(..)   , sqlOR   , sqlConcatComma@@ -289,32 +218,12 @@ -- structure of a condition. For now it seems that the only -- interesting case is EXISTS (SELECT ...), because that internal -- SELECT can have explainable clauses.-data SqlCondition = SqlPlainCondition SQL SqlWhyNot+data SqlCondition = SqlPlainCondition SQL                   | SqlExistsCondition SqlSelect                     deriving (Typeable, Show) --- | 'SqlWhyNot' contains a recipe for how to query the database for--- some values we're interested in and construct a proper exception--- object using that information. For @SqlWhyNot mkException queries@--- the @mkException@ should take as input a list of the same length--- list as there are queries. Each query will be run in a JOIN context--- with all referenced tables, so it can extract values from there.-data SqlWhyNot =-  forall e row. (FromRow row, Exception e) =>-  SqlWhyNot Bool (row -> e) [SQL]--{--instance Eq SqlCondition where-  (SqlPlainCondition a _) == (SqlPlainCondition b _) = a == b-  (SqlExistsCondition a) == (SqlExistsCondition b) = a == b-  _ == _ = False-  -}--instance Show SqlWhyNot where-  show (SqlWhyNot _important exc expr) = "SqlWhyNot " ++ show (typeOf (exc $undefined)) ++ " " ++ show expr- instance Sqlable SqlCondition where-  toSQLCommand (SqlPlainCondition a _) = a+  toSQLCommand (SqlPlainCondition a) = a   toSQLCommand (SqlExistsCondition a) = "EXISTS (" <> toSQLCommand (a { sqlSelectResult = ["TRUE"] }) <> ")"  data SqlSelect = SqlSelect@@ -349,18 +258,19 @@   }  data SqlInsertSelect = SqlInsertSelect-  { sqlInsertSelectWhat     :: SQL-  , sqlInsertSelectDistinct :: Bool-  , sqlInsertSelectSet      :: [(SQL, SQL)]-  , sqlInsertSelectResult   :: [SQL]-  , sqlInsertSelectFrom     :: SQL-  , sqlInsertSelectWhere    :: [SqlCondition]-  , sqlInsertSelectOrderBy  :: [SQL]-  , sqlInsertSelectGroupBy  :: [SQL]-  , sqlInsertSelectHaving   :: [SQL]-  , sqlInsertSelectOffset   :: Integer-  , sqlInsertSelectLimit    :: Integer-  , sqlInsertSelectWith     :: [(SQL, SQL)]+  { sqlInsertSelectWhat       :: SQL+  , sqlInsertSelectOnConflict :: Maybe (SQL, Maybe SQL)+  , sqlInsertSelectDistinct   :: Bool+  , sqlInsertSelectSet        :: [(SQL, SQL)]+  , sqlInsertSelectResult     :: [SQL]+  , sqlInsertSelectFrom       :: SQL+  , sqlInsertSelectWhere      :: [SqlCondition]+  , sqlInsertSelectOrderBy    :: [SQL]+  , sqlInsertSelectGroupBy    :: [SQL]+  , sqlInsertSelectHaving     :: [SQL]+  , sqlInsertSelectOffset     :: Integer+  , sqlInsertSelectLimit      :: Integer+  , sqlInsertSelectWith       :: [(SQL, SQL)]   }  data SqlDelete = SqlDelete@@ -459,20 +369,21 @@       orderByClause = emitClausesSepComma "ORDER BY" $ sqlSelectOrderBy cmd       limitClause   = unsafeSQL $ "LIMIT" <+> show (sqlSelectLimit cmd) +emitClauseOnConflictForInsert :: Maybe (SQL, Maybe SQL) -> SQL+emitClauseOnConflictForInsert = \case+       Nothing                   -> ""+       Just (condition, maction) -> emitClause "ON CONFLICT" $+         condition <+> "DO" <+> fromMaybe "NOTHING" maction+ instance Sqlable SqlInsert where   toSQLCommand cmd =     emitClausesSepComma "WITH" (map (\(name,command) -> name <+> "AS" <+> parenthesize command) (sqlInsertWith cmd)) <+>     "INSERT INTO" <+> sqlInsertWhat cmd <+>     parenthesize (sqlConcatComma (map fst (sqlInsertSet cmd))) <+>     emitClausesSep "VALUES" "," (map sqlConcatComma (transpose (map (makeLongEnough . snd) (sqlInsertSet cmd)))) <+>-    emitClauseOnConflict (sqlInsertOnConflict cmd) <+>+    emitClauseOnConflictForInsert (sqlInsertOnConflict cmd) <+>     emitClausesSepComma "RETURNING" (sqlInsertResult cmd)    where-     emitClauseOnConflict = \case-       Nothing                   -> ""-       Just (condition, maction) -> emitClause "ON CONFLICT" $-         condition <+> "DO" <+> fromMaybe "NOTHING" maction-      -- this is the longest list of values      longest = maximum (1 : (map (lengthOfEither . snd) (sqlInsertSet cmd)))      lengthOfEither (Single _) = 1@@ -500,6 +411,7 @@                                               , sqlSelectLimit   = sqlInsertSelectLimit cmd                                               , sqlSelectWith    = []                                               }+    , emitClauseOnConflictForInsert (sqlInsertSelectOnConflict cmd)     , emitClausesSepComma "RETURNING" $ sqlInsertSelectResult cmd     ] @@ -541,18 +453,19 @@ sqlInsertSelect :: SQL -> SQL -> State SqlInsertSelect () -> SqlInsertSelect sqlInsertSelect table from refine =   execState refine (SqlInsertSelect-                    { sqlInsertSelectWhat    = table-                    , sqlInsertSelectDistinct = False-                    , sqlInsertSelectSet     = []-                    , sqlInsertSelectResult  = []-                    , sqlInsertSelectFrom    = from-                    , sqlInsertSelectWhere   = []-                    , sqlInsertSelectOrderBy = []-                    , sqlInsertSelectGroupBy = []-                    , sqlInsertSelectHaving  = []-                    , sqlInsertSelectOffset  = 0-                    , sqlInsertSelectLimit   = -1-                    , sqlInsertSelectWith    = []+                    { sqlInsertSelectWhat       = table+                    , sqlInsertSelectOnConflict = Nothing+                    , sqlInsertSelectDistinct   = False+                    , sqlInsertSelectSet        = []+                    , sqlInsertSelectResult     = []+                    , sqlInsertSelectFrom       = from+                    , sqlInsertSelectWhere      = []+                    , sqlInsertSelectOrderBy    = []+                    , sqlInsertSelectGroupBy    = []+                    , sqlInsertSelectHaving     = []+                    , sqlInsertSelectOffset     = 0+                    , sqlInsertSelectLimit      = -1+                    , sqlInsertSelectWith       = []                     })  sqlUpdate :: SQL -> State SqlUpdate () -> SqlUpdate@@ -616,103 +529,26 @@   sqlWhere1 cmd cond = cmd { sqlAllWhere = sqlAllWhere cmd ++ [cond] }   sqlGetWhereConditions = sqlAllWhere -newtype SqlWhereIgnore a = SqlWhereIgnore { unSqlWhereIgnore :: a }---ignoreWhereClause :: SqlCondition -> SqlCondition-ignoreWhereClause (SqlPlainCondition sql (SqlWhyNot _b f s)) =-  SqlPlainCondition sql (SqlWhyNot False f s)-ignoreWhereClause (SqlExistsCondition sql) =-  SqlExistsCondition (sql { sqlSelectWhere = map ignoreWhereClause (sqlSelectWhere sql)})--instance (SqlWhere a) => SqlWhere (SqlWhereIgnore a) where-  sqlWhere1 (SqlWhereIgnore cmd) cond =-        SqlWhereIgnore (sqlWhere1 cmd (ignoreWhereClause cond))-  sqlGetWhereConditions (SqlWhereIgnore cmd) = sqlGetWhereConditions cmd---sqlIgnore :: (MonadState s m)-          => State (SqlWhereIgnore s) a-          -> m ()-sqlIgnore clauses = modify (\cmd -> unSqlWhereIgnore (execState clauses (SqlWhereIgnore cmd)))- -- | The @WHERE@ part of an SQL query. See above for a usage -- example. See also 'SqlCondition'. sqlWhere :: (MonadState v m, SqlWhere v) => SQL -> m ()-sqlWhere sql = sqlWhereE (DBBaseLineConditionIsFalse sql) sql---- | Like 'sqlWhere', but also takes an exception value that is thrown--- in case of error. See 'SqlCondition' and 'SqlWhyNot'.-sqlWhereE :: (MonadState v m, SqlWhere v, Exception e) => e -> SQL -> m ()-sqlWhereE exc sql = modify (\cmd -> sqlWhere1 cmd (SqlPlainCondition sql (SqlWhyNot True exc2 [])))-  where-    exc2 (_::()) = exc---- | Like 'sqlWhereE', but takes a one-argument function that--- constructs an exception value plus an SQL fragment for querying the--- database for the argument that is fed into the exception--- constructor function. See 'SqlCondition' and 'SqlWhyNot'.------ The SQL fragment should be of form @TABLENAME.COLUMNAME@, as it is--- executed as part of a @SELECT@ query involving all referenced--- tables.-sqlWhereEV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a) => (a -> e, SQL) -> SQL -> m ()-sqlWhereEV (exc, vsql) sql = modify (\cmd -> sqlWhere1 cmd (SqlPlainCondition sql (SqlWhyNot True exc2 [vsql])))-  where-    exc2 (Identity v1) = exc v1---- | Like 'sqlWhereEV', but the exception constructor function takes--- two arguments.-sqlWhereEVV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a, FromSQL b) => (a -> b -> e, SQL, SQL) -> SQL -> m ()-sqlWhereEVV (exc, vsql1, vsql2) sql = modify (\cmd -> sqlWhere1 cmd (SqlPlainCondition sql (SqlWhyNot True exc2 [vsql1, vsql2])))-  where-    exc2 (v1, v2) = exc v1 v2---- | Like 'sqlWhereEV', but the exception constructor function takes--- three arguments.-sqlWhereEVVV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a, FromSQL b, FromSQL c) => (a -> b -> c -> e, SQL, SQL, SQL) -> SQL -> m ()-sqlWhereEVVV (exc, vsql1, vsql2, vsql3) sql = modify (\cmd -> sqlWhere1 cmd (SqlPlainCondition sql (SqlWhyNot True exc2 [vsql1, vsql2, vsql3])))-  where-    exc2 (v1, v2, v3) = exc v1 v2 v3---- | Like 'sqlWhereEV', but the exception constructor function takes--- four arguments.-sqlWhereEVVVV :: (MonadState v m, SqlWhere v, Exception e, FromSQL a, FromSQL b, FromSQL c, FromSQL d) => (a -> b -> c -> d -> e, SQL, SQL, SQL, SQL) -> SQL -> m ()-sqlWhereEVVVV (exc, vsql1, vsql2, vsql3, vsql4) sql = modify (\cmd -> sqlWhere1 cmd (SqlPlainCondition sql (SqlWhyNot True exc2 [vsql1, vsql2, vsql3, vsql4])))-  where-    exc2 (v1, v2, v3, v4) = exc v1 v2 v3 v4+sqlWhere sql = modify (\cmd -> sqlWhere1 cmd (SqlPlainCondition sql))  sqlWhereEq :: (MonadState v m, SqlWhere v, Show a, ToSQL a) => SQL -> a -> m () sqlWhereEq name value = sqlWhere $ name <+> "=" <?> value -sqlWhereEqE :: (MonadState v m, SqlWhere v, Exception e, Show a, FromSQL a, ToSQL a)-            => (a -> a -> e) -> SQL -> a -> m ()-sqlWhereEqE exc name value = sqlWhereEV (exc value, name) $ name <+> "=" <?> value- sqlWhereEqSql :: (MonadState v m, SqlWhere v, Sqlable sql) => SQL -> sql -> m () sqlWhereEqSql name1 name2 = sqlWhere $ name1 <+> "=" <+> toSQLCommand name2  sqlWhereNotEq :: (MonadState v m, SqlWhere v, Show a, ToSQL a) => SQL -> a -> m () sqlWhereNotEq name value = sqlWhere $ name <+> "<>" <?> value -sqlWhereNotEqE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a)-               => (a -> a -> e) -> SQL -> a -> m ()-sqlWhereNotEqE exc name value = sqlWhereEV (exc value, name) $ name <+> "<>" <?> value- sqlWhereLike :: (MonadState v m, SqlWhere v, Show a, ToSQL a) => SQL -> a -> m () sqlWhereLike name value = sqlWhere $ name <+> "LIKE" <?> value -sqlWhereLikeE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a)-              => (a -> a -> e) -> SQL -> a -> m ()-sqlWhereLikeE exc name value = sqlWhereEV (exc value, name) $ name <+> "LIKE" <?> value- sqlWhereILike :: (MonadState v m, SqlWhere v, Show a, ToSQL a) => SQL -> a -> m () sqlWhereILike name value = sqlWhere  $ name <+> "ILIKE" <?> value -sqlWhereILikeE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a)-               => (a -> a -> e) -> SQL -> a -> m ()-sqlWhereILikeE exc name value = sqlWhereEV (exc value, name) $ name <+> "ILIKE" <?> value- sqlWhereIn :: (MonadState v m, SqlWhere v, Show a, ToSQL a) => SQL -> [a] -> m () sqlWhereIn _name [] = sqlWhere "FALSE" sqlWhereIn name [value] = sqlWhereEq name value@@ -723,13 +559,6 @@ sqlWhereInSql :: (MonadState v m, Sqlable a, SqlWhere v) => SQL -> a -> m () sqlWhereInSql name sql = sqlWhere $ name <+> "IN" <+> parenthesize (toSQLCommand sql) -sqlWhereInE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a)-            => ([a] -> a -> e) -> SQL -> [a] -> m ()-sqlWhereInE exc name [] = sqlWhereEV (exc [], name) "FALSE"-sqlWhereInE exc name [value] = sqlWhereEqE (exc . (\x -> [x])) name value-sqlWhereInE exc name values =-  sqlWhereEV (exc values, name) $ name <+> "IN (SELECT UNNEST(" <?> Array1 values <+> "))"- sqlWhereNotIn :: (MonadState v m, SqlWhere v, Show a, ToSQL a) => SQL -> [a] -> m () sqlWhereNotIn _name [] = sqlWhere "TRUE" sqlWhereNotIn name [value] = sqlWhereNotEq name value@@ -738,13 +567,6 @@ sqlWhereNotInSql :: (MonadState v m, Sqlable a, SqlWhere v) => SQL -> a -> m () sqlWhereNotInSql name sql = sqlWhere $ name <+> "NOT IN" <+> parenthesize (toSQLCommand sql) -sqlWhereNotInE :: (MonadState v m, SqlWhere v, Exception e, Show a, ToSQL a, FromSQL a)-               => ([a] -> a -> e) -> SQL -> [a] -> m ()-sqlWhereNotInE exc name [] = sqlWhereEV (exc [], name) "TRUE"-sqlWhereNotInE exc name [value] = sqlWhereNotEqE (exc . (\x -> [x])) name value-sqlWhereNotInE exc name values =-  sqlWhereEV (exc values, name) $ name <+> "NOT IN (SELECT UNNEST(" <?> Array1 values <+> "))"- sqlWhereExists :: (MonadState v m, SqlWhere v) => SqlSelect -> m () sqlWhereExists sql = do   modify (\cmd -> sqlWhere1 cmd (SqlExistsCondition sql))@@ -759,21 +581,11 @@ sqlWhereIsNotNULL :: (MonadState v m, SqlWhere v) => SQL -> m () sqlWhereIsNotNULL col = sqlWhere $ col <+> "IS NOT NULL" -sqlWhereIsNULLE :: (MonadState v m, SqlWhere v, Exception e, FromSQL a)-                => (a -> e) -> SQL -> m ()-sqlWhereIsNULLE exc col = sqlWhereEV (exc, col) $ col <+> "IS NULL"- -- | Add a condition in the WHERE statement that holds if any of the given -- condition holds. sqlWhereAny :: (MonadState v m, SqlWhere v) => [State SqlAll ()] -> m () sqlWhereAny = sqlWhere . sqlWhereAnyImpl --- | Add a condition just like 'sqlWhereAny' but throw the given exception if--- none of the given conditions hold.-sqlWhereAnyE :: (Exception e, MonadState v m, SqlWhere v)-             => e -> [State SqlAll ()] -> m ()-sqlWhereAnyE e = sqlWhereE e . sqlWhereAnyImpl- sqlWhereAnyImpl :: [State SqlAll ()] -> SQL sqlWhereAnyImpl [] = "FALSE" sqlWhereAnyImpl l =@@ -837,22 +649,6 @@ instance SqlSet SqlInsertSelect where   sqlSet1 cmd name v = cmd { sqlInsertSelectSet = sqlInsertSelectSet cmd ++ [(name, v)] } -sqlOnConflictDoNothing :: MonadState SqlInsert m => m ()-sqlOnConflictDoNothing = modify $ \cmd -> cmd-  { sqlInsertOnConflict = Just ("", Nothing)-  }--sqlOnConflictOnColumns-  :: Sqlable sql => MonadState SqlInsert m => [SQL] -> sql -> m ()-sqlOnConflictOnColumns columns sql = modify $ \cmd -> cmd-  { sqlInsertOnConflict = Just (parenthesize $ sqlConcatComma columns, Just $ toSQLCommand sql)-  }--sqlOnConflictOnColumnsDoNothing :: MonadState SqlInsert m => [SQL] -> m ()-sqlOnConflictOnColumnsDoNothing columns = modify $ \cmd -> cmd-  { sqlInsertOnConflict = Just (parenthesize $ sqlConcatComma columns, Nothing)-  }- sqlSetCmd :: (MonadState v m, SqlSet v) => SQL -> SQL -> m () sqlSetCmd name sql = modify (\cmd -> sqlSet1 cmd name sql) @@ -874,6 +670,36 @@ sqlCopyColumn :: (MonadState v m, SqlSet v) => SQL -> m () sqlCopyColumn column = sqlSetCmd column column +class SqlOnConflict a where+  sqlOnConflictDoNothing1 :: a -> a+  sqlOnConflictOnColumnsDoNothing1 :: a -> [SQL] -> a+  sqlOnConflictOnColumns1 :: Sqlable sql => a -> [SQL] -> sql -> a++instance SqlOnConflict SqlInsert where+  sqlOnConflictDoNothing1 cmd = +    cmd { sqlInsertOnConflict = Just ("", Nothing) }+  sqlOnConflictOnColumns1 cmd columns sql = +    cmd { sqlInsertOnConflict = Just (parenthesize $ sqlConcatComma columns, Just $ toSQLCommand sql) }+  sqlOnConflictOnColumnsDoNothing1 cmd columns = +    cmd { sqlInsertOnConflict = Just (parenthesize $ sqlConcatComma columns, Nothing) }++instance SqlOnConflict SqlInsertSelect where+  sqlOnConflictDoNothing1 cmd = +    cmd { sqlInsertSelectOnConflict = Just ("", Nothing) }+  sqlOnConflictOnColumns1 cmd columns sql = +    cmd { sqlInsertSelectOnConflict = Just (parenthesize $ sqlConcatComma columns, Just $ toSQLCommand sql) }+  sqlOnConflictOnColumnsDoNothing1 cmd columns = +    cmd { sqlInsertSelectOnConflict = Just (parenthesize $ sqlConcatComma columns, Nothing) }++sqlOnConflictDoNothing :: (MonadState v m, SqlOnConflict v) => m ()+sqlOnConflictDoNothing = modify sqlOnConflictDoNothing1++sqlOnConflictOnColumnsDoNothing :: (MonadState v m, SqlOnConflict v) => [SQL] -> m ()+sqlOnConflictOnColumnsDoNothing columns = modify (\cmd -> sqlOnConflictOnColumnsDoNothing1 cmd columns)++sqlOnConflictOnColumns :: (MonadState v m, SqlOnConflict v, Sqlable sql) => [SQL] -> sql -> m ()+sqlOnConflictOnColumns columns sql = modify (\cmd -> sqlOnConflictOnColumns1 cmd columns sql)+ class SqlResult a where   sqlResult1 :: a -> SQL -> a @@ -889,8 +715,6 @@ instance SqlResult SqlUpdate where   sqlResult1 cmd sql = cmd { sqlUpdateResult = sqlUpdateResult cmd ++ [sql] } -- sqlResult :: (MonadState v m, SqlResult v) => SQL -> m () sqlResult sql = modify (\cmd -> sqlResult1 cmd sql) @@ -955,319 +779,3 @@  sqlDistinct :: (MonadState v m, SqlDistinct v) => m () sqlDistinct = modify (\cmd -> sqlDistinct1 cmd)---class (SqlWhere a, Sqlable a) => SqlTurnIntoSelect a where-  sqlTurnIntoSelect :: a -> SqlSelect--instance SqlTurnIntoSelect SqlSelect where-  sqlTurnIntoSelect = id----- | The 'sqlTurnIntoWhyNotSelect' turns a failed query into a--- why-not-query that can explain why query altered zero rows or--- returned zero results.------ Lets consider an example of explanation:------ > UPDATE t1--- >    SET a = 1--- >  WHERE cond1--- >    AND cond2                       -- with value2--- >    AND EXISTS (SELECT TRUE--- >                  FROM t2--- >                 WHERE cond3        -- with value3a and value3b--- >                   AND EXISTS (SELECT TRUE--- >                                 FROM t3--- >                                WHERE cond4))------ 'sqlTurnIntoWhyNotSelect' will produce a @SELECT@ of the form:------ > SELECT--- >   EXISTS (SELECT TRUE ... WHERE cond1),--- >   EXISTS (SELECT TRUE ... WHERE cond1 AND cond2),--- >   EXISTS (SELECT TRUE ... WHERE cond1 AND cond2 AND cond3),--- >   EXISTS (SELECT TRUE ... WHERE cond1 AND cond2 AND cond3 AND cond4);------ Now, after this statement is executed we see which of these--- returned @FALSE@ as the first one. This is the condition that failed--- the whole query.------ We can get more information at this point. If failed condition was--- @cond2@, then @value2@ can be extracted by this statement:------ > SELECT value2 ... WHERE cond1;------ If failed condition was @cond3@, then statement executed can be:------ > SELECT value3a, value3b ... WHERE cond1 AND cond2;------ Rationale: @EXISTS@ clauses should pinpoint which @condX@ was the first--- one to produce zero rows.  @SELECT@ clauses after @EXISTS@ should--- explain why condX filtered out all rows.------ 'DB.WhyNot.kWhyNot1' looks for first @EXISTS@ clause that is @FALSE@--- and then tries to construct an @Exception@ object with values that come--- after. If values that comes after cannot be sensibly parsed--- (usually they are @NULL@ when a value is expected), this exception is--- skipped and next one is tried.------ If first @EXISTS@ clause is @TRUE@ but no other exception was properly--- generated then @DBExceptionCouldNotParseValues@ is thrown with pair--- of 'typeRef' of first exception that could not be parsed and with--- list of SqlValues that it could not parse.------ The 'DB.WhyNot.kRun1OrThrowWhyNot' throws first exception on the--- list.------ We have a theorem to use in this transformation:------ > EXISTS (SELECT t1 WHERE cond1 AND EXISTS (SELECT t2 WHERE cond2))------ is equivalent to------ > EXISTS (SELECT t1, t2 WHERE cond1 AND cond2)------ and it can be used recursivelly.-sqlTurnIntoWhyNotSelect :: (SqlTurnIntoSelect a) => a -> SqlSelect-sqlTurnIntoWhyNotSelect command =-    sqlSelect "" . sqlResult $ mconcat [-        "ARRAY["-      , mintercalate ", " $ map emitExists [0..(count-1)]-      , "]::boolean[]"-      ]-    where select = sqlTurnIntoSelect command-          count :: Int-          count = sum (map count' (sqlSelectWhere select))-          count' (SqlPlainCondition {}) = 1-          count' (SqlExistsCondition select') = sum (map count' (sqlSelectWhere select'))--          emitExists :: Int -> SQL-          emitExists current =-            case runState (run current select) 0 of-              (s, _) -> if null (sqlSelectWhere s)-                        then "TRUE"-                        else "EXISTS (" <> (toSQLCommand $ s { sqlSelectResult = [ "TRUE" ]}) <> ")"--          run :: (MonadState Int m) => Int -> SqlSelect -> m SqlSelect-          run current select' = do-            new <- mapM (around current) (sqlSelectWhere select')-            return (select' { sqlSelectWhere = concat new })--          around :: (MonadState Int m) => Int -> SqlCondition -> m [SqlCondition]-          around current cond@(SqlPlainCondition{}) = do-            index <- get-            modify (+1)-            if current >= index-              then return [cond]-              else return []-          around current (SqlExistsCondition subSelect) = do-            subSelect' <- run current subSelect-            return [SqlExistsCondition subSelect']---instance SqlTurnIntoSelect SqlUpdate where-  sqlTurnIntoSelect s = SqlSelect-                        { sqlSelectFrom    = sqlUpdateWhat s <>-                                             if isSqlEmpty (sqlUpdateFrom s)-                                             then ""-                                             else "," <+> sqlUpdateFrom s-                        , sqlSelectUnion    = []-                        , sqlSelectDistinct = False-                        , sqlSelectResult  = if null (sqlUpdateResult s)-                                             then ["TRUE"]-                                             else sqlUpdateResult s-                        , sqlSelectWhere   = sqlUpdateWhere s-                        , sqlSelectOrderBy = []-                        , sqlSelectGroupBy = []-                        , sqlSelectHaving  = []-                        , sqlSelectOffset  = 0-                        , sqlSelectLimit   = -1-                        , sqlSelectWith    = sqlUpdateWith s -- this is a bit dangerous because it can contain nested DELETE/UPDATE-                        }--instance SqlTurnIntoSelect SqlDelete where-  sqlTurnIntoSelect s = SqlSelect-                        { sqlSelectFrom    = sqlDeleteFrom s <>-                                             if isSqlEmpty (sqlDeleteUsing s)-                                             then ""-                                             else "," <+> sqlDeleteUsing s-                        , sqlSelectUnion    = []-                        , sqlSelectDistinct = False-                        , sqlSelectResult  = if null (sqlDeleteResult s)-                                             then ["TRUE"]-                                             else sqlDeleteResult s-                        , sqlSelectWhere   = sqlDeleteWhere s-                        , sqlSelectOrderBy = []-                        , sqlSelectGroupBy = []-                        , sqlSelectHaving  = []-                        , sqlSelectOffset  = 0-                        , sqlSelectLimit   = -1-                        , sqlSelectWith    = sqlDeleteWith s -- this is a bit dangerous because it can contain nested DELETE/UPDATE-                        }--instance SqlTurnIntoSelect SqlInsertSelect where-  sqlTurnIntoSelect s = SqlSelect-                        { sqlSelectFrom    = sqlInsertSelectFrom s-                        , sqlSelectUnion   = []-                        , sqlSelectDistinct = False-                        , sqlSelectResult  = sqlInsertSelectResult s-                        , sqlSelectWhere   = sqlInsertSelectWhere s-                        , sqlSelectOrderBy = sqlInsertSelectOrderBy s-                        , sqlSelectGroupBy = sqlInsertSelectGroupBy s-                        , sqlSelectHaving  = sqlInsertSelectHaving s-                        , sqlSelectOffset  = sqlInsertSelectOffset s-                        , sqlSelectLimit   = sqlInsertSelectLimit s-                        , sqlSelectWith    = sqlInsertSelectWith s -- this is a bit dangerous because it can contain nested DELETE/UPDATE-                        }--{- Warning: use kWhyNot1 for now as kWhyNot does not work in expected way.--kWhyNot should return a list of rows, where each row is a list of-exceptions.  Right now we are not able to differentiate between rows-because we do not support a concept of a row identity. kWhyNot can-return rows in any order, returns empty rows for successful hits, does-not return a row if baseline conditions weren't met. This effectivelly-renders it useless.--kWhyNot will be resurrected when we get a row identity concept.---}--{---- | If 'kWhyNot1' returns an empty list of exceptions when none of--- @EXISTS@ clauses generated by 'sqlTurnIntoWhyNotSelect' was--- @FALSE@. Should not happen in real life, file a bug report if you see--- such a case.-kWhyNot :: (SqlTurnIntoSelect s, MonadDB m) => s -> m [[SomeException]]-kWhyNot cmd = do-  let newSelect = sqlTurnIntoWhyNotSelect cmd-  if null (sqlSelectResult newSelect)-     then return [[]]-     else do-       kRun_ newSelect-       kFold2 (decodeListOfExceptionsFromWhere (sqlGetWhereConditions cmd)) []--}---data ExceptionMaker = forall row. FromRow row => ExceptionMaker (row -> SomeException)--newtype DBKwhyNotInternalError = DBKwhyNotInternalError String-  deriving (Show, Typeable)--instance Exception DBKwhyNotInternalError--kWhyNot1Ex :: forall m s. (SqlTurnIntoSelect s, MonadDB m, MonadThrow m)-           => s -> m (Bool, SomeException)-kWhyNot1Ex cmd = do-  let newSelect = sqlTurnIntoSelect cmd-      newWhyNotSelect = sqlTurnIntoWhyNotSelect newSelect-  let findFirstFalse :: Identity (Array1 Bool) -> Int-      findFirstFalse (Identity (Array1 row)) = fromMaybe 0 (findIndex (== False) row)-  runQuery_ (newWhyNotSelect { sqlSelectLimit = 1 })-  indexOfFirstFailedCondition <- fetchOne findFirstFalse--  let logics = enumerateWhyNotExceptions ((sqlSelectFrom newSelect),[]) (sqlGetWhereConditions newSelect)--  let mcondition = logics `atMay` indexOfFirstFailedCondition--  case mcondition of-    Nothing -> return-      (True, toException . DBKwhyNotInternalError $-        "list of failed conditions is empty")-    Just (important, ExceptionMaker exception, _from, []) ->-      return (important, exception $ error "this argument should've been ignored")-    Just (important, ExceptionMaker exception, (from, conds), sqls) -> do-       let statement' = sqlSelect2 from $ do-             mapM_ sqlResult sqls-             sqlLimit (1::Int)-             sqlOffset (0::Int)-           statement = statement' { sqlSelectWhere = conds }-       --Log.debug $ "Explanation SQL:\n" ++ show statement-       runQuery_ statement-       result <- fetchOne exception-       return (important, result)---- | Function 'kWhyNot1' is a workhorse for explainable SQL--- failures. SQL fails if it did not affect any rows or did not return--- any rows.  When that happens 'kWhyNot1' should be called. 'kWhyNot1'--- returns an exception describing why a row could not be--- returned or affected by a query.-kWhyNot1 :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m)-         => s -> m SomeException-kWhyNot1 cmd = snd `fmap` kWhyNot1Ex cmd--enumerateWhyNotExceptions :: (SQL, [SqlCondition])-                          -> [SqlCondition]-                          -> [( Bool-                              , ExceptionMaker-                              , (SQL, [SqlCondition])-                              , [SQL]-                              )]-enumerateWhyNotExceptions (from,condsUpTillNow) conds = concatMap worker (zip conds (inits conds))-  where-    worker (SqlPlainCondition _ (SqlWhyNot b f s), condsUpTillNow2) =-      [(b, ExceptionMaker (SomeException . f), (from, condsUpTillNow ++ condsUpTillNow2), s)]-    worker (SqlExistsCondition s, condsUpTillNow2) =-      enumerateWhyNotExceptions (newFrom, condsUpTillNow ++ condsUpTillNow2)-                                  (sqlGetWhereConditions s)-      where-        newFrom = if isSqlEmpty from-                  then sqlSelectFrom s-                  else if isSqlEmpty (sqlSelectFrom s)-                       then from-                       else from <> ", " <> sqlSelectFrom s---- | Implicit exception for `sqlWhere` combinator family.-newtype DBBaseLineConditionIsFalse = DBBaseLineConditionIsFalse SQL-  deriving (Show, Typeable)--instance Exception DBBaseLineConditionIsFalse where-  fromException se@(SomeException e) = msum-    [ cast e-    , do-      DBException {..} <- fromException se-      fromException . toException $ dbeError-    ]--kRunManyOrThrowWhyNot :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m)-                   => s -> m ()-kRunManyOrThrowWhyNot sqlable = do-  success <- runQuery $ toSQLCommand sqlable-  when (success == 0) $ do-    SomeException exception <- kWhyNot1 sqlable-    throwDB exception---kRun1OrThrowWhyNot :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m)-                   => s -> m ()-kRun1OrThrowWhyNot sqlable = do-  success <- runQuery01 $ toSQLCommand sqlable-  when (not success) $ do-    SomeException exception <- kWhyNot1 sqlable-    throwDB exception---kRun1OrThrowWhyNotAllowIgnore :: (SqlTurnIntoSelect s, MonadDB m, MonadThrow m)-                                => s -> m ()-kRun1OrThrowWhyNotAllowIgnore sqlable = do-  success <- runQuery01 $ toSQLCommand sqlable-  when (not success) $ do-    (important, SomeException exception) <- kWhyNot1Ex sqlable-    when (important) $-      throwDB exception--kRunAndFetch1OrThrowWhyNot :: (IsSQL s, FromRow row, MonadDB m, MonadThrow m, SqlTurnIntoSelect s)-                           => (row -> a) -> s -> m a-kRunAndFetch1OrThrowWhyNot decoder sqlcommand = do-  runQuery_ sqlcommand-  results <- fetchMany decoder-  case results of-    [] -> do-      SomeException exception <- kWhyNot1 sqlcommand-      throwDB exception-    [r] -> return r-    _ -> throwDB AffectedRowsMismatch {-      rowsExpected = [(1, 1)]-    , rowsDelivered = length results-    }
test/Main.hs view
@@ -514,11 +514,61 @@    -- If NO CONFLICT is not specified, make sure we throw an exception.   eres :: Either DBException () <- E.try . withSavepoint "testDBSchema" $ do-    runQuery_ . sqlInsert "bank " $ do+    runQuery_ . sqlInsert "bank" $ do       sqlSet "id" bankId       sqlSet "name" name       sqlSet "location" location   liftIO $ assertBool "If ON CONFLICT is not specified an exception is thrown" (isLeft eres)++  runQuery_ . sqlInsertSelect "bank" "bank" $ do+    sqlSetCmd "id" "id"+    sqlSetCmd "name" "name"+    sqlSetCmd "location" "location"+    sqlWhereEq "id" bankId+    sqlOnConflictOnColumns ["id"] . sqlUpdate "" $ do+      sqlSet "name" name+      sqlSet "location" location+  runQuery_ . sqlSelect "bank" $ do+    sqlResult "name"+    sqlResult "location"+    sqlWhereEq "id" bankId+  details5 <- fetchOne id+  liftIO $ assertEqual "INSERT ON CONFLICT updates" (name, location) details5++  runQuery_ . sqlInsertSelect "bank" "bank" $ do+    sqlSetCmd "id" "id"+    sqlSetCmd "name" "name"+    sqlSetCmd "location" "location"+    sqlWhereEq "id" bankId+    sqlOnConflictDoNothing+  runQuery_ . sqlSelect "bank" $ do+    sqlResult "name"+    sqlResult "location"+    sqlWhereEq "id" bankId+  details6 <- fetchOne id+  liftIO $ assertEqual "INSERT ON CONFLICT does nothing (1)" (name, location) details6++  runQuery_ . sqlInsertSelect "bank" "bank" $ do+    sqlSetCmd "id" "id"+    sqlSetCmd "name" "name"+    sqlSetCmd "location" "location"+    sqlWhereEq "id" bankId+    sqlOnConflictOnColumnsDoNothing ["id"]+  runQuery_ . sqlSelect "bank" $ do+    sqlResult "name"+    sqlResult "location"+    sqlWhereEq "id" bankId+  details7 <- fetchOne id+  liftIO $ assertEqual "INSERT ON CONFLICT does nothing (2)" (name, location) details7++  -- If NO CONFLICT is not specified, make sure we throw an exception.+  eres1 :: Either DBException () <- E.try . withSavepoint "testDBSchema" $ do+    runQuery_ . sqlInsertSelect "bank" "bank" $ do+      sqlSetCmd "id" "id"+      sqlSetCmd "name" "name"+      sqlSetCmd "location" "location"+      sqlWhereEq "id" bankId+  liftIO $ assertBool "If ON CONFLICT is not specified an exception is thrown" (isLeft eres1)    -- Populate the 'bad_guy' table.   runQuery_ . sqlInsert "bad_guy" $ do