module Hasql.Engine.Errors where

import Hasql.Comms.Recv qualified
import Hasql.Comms.ResultDecoder qualified
import Hasql.Comms.Roundtrip qualified
import Hasql.Comms.RowReader qualified
import Hasql.Platform.Prelude
import TextBuilder qualified

-- * Error types

-- |
-- Error that occurs when attempting to establish a database connection.
--
-- These errors can occur when calling 'Hasql.Connection.acquire'.
-- Connection errors are categorized into several types to help with
-- error handling and logging.
data ConnectionError
  = -- | Network-level error while connecting to the database server.
    --
    -- This typically indicates issues like:
    --
    -- * Server is not reachable (wrong host\/port or server is down)
    -- * Network connectivity problems
    -- * Firewall blocking the connection
    -- * Connection timeout
    --
    -- These errors are transient and the operation can be retried.
    NetworkingConnectionError
      -- | Human readable details intended for logging.
      Text
  | -- | Authentication failed when connecting to the database.
    --
    -- This typically indicates issues like:
    --
    -- * Invalid username or password
    -- * User does not have permission to access the database
    -- * Authentication method mismatch (e.g., server requires SSL but client doesn't use it)
    --
    -- These errors are not transient and require fixing the credentials or permissions.
    AuthenticationConnectionError
      -- | Human readable details intended for logging.
      Text
  | -- | Compatibility issue between client and server.
    --
    -- This typically indicates issues like:
    --
    -- * Server version is too old or too new
    -- * Required PostgreSQL features are not available
    -- * Protocol version mismatch
    --
    -- These errors are not transient and require upgrading/downgrading
    -- the server or client.
    CompatibilityConnectionError
      -- | Human readable details intended for logging.
      Text
  | -- | Uncategorized error coming from @libpq@.
    --
    -- This is a catch-all for connection errors that don't fit into other categories.
    -- The error message may be empty if @libpq@ doesn't provide details.
    --
    -- These errors are not transient by default.
    OtherConnectionError
      -- | Human readable details intended for logging. May be empty.
      Text
  deriving stock (Int -> ConnectionError -> ShowS
[ConnectionError] -> ShowS
ConnectionError -> String
(Int -> ConnectionError -> ShowS)
-> (ConnectionError -> String)
-> ([ConnectionError] -> ShowS)
-> Show ConnectionError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ConnectionError -> ShowS
showsPrec :: Int -> ConnectionError -> ShowS
$cshow :: ConnectionError -> String
show :: ConnectionError -> String
$cshowList :: [ConnectionError] -> ShowS
showList :: [ConnectionError] -> ShowS
Show, ConnectionError -> ConnectionError -> Bool
(ConnectionError -> ConnectionError -> Bool)
-> (ConnectionError -> ConnectionError -> Bool)
-> Eq ConnectionError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ConnectionError -> ConnectionError -> Bool
== :: ConnectionError -> ConnectionError -> Bool
$c/= :: ConnectionError -> ConnectionError -> Bool
/= :: ConnectionError -> ConnectionError -> Bool
Eq)

-- |
-- Error that occurs during session execution.
--
-- A session is a batch of actions executed in a database connection context.
-- Session errors can occur due to statement failures, connection issues,
-- missing types, or internal driver errors.
--
-- Session errors provide detailed context to help diagnose problems,
-- including SQL text, parameters, and the location of the error within
-- a pipeline of statements.
data SessionError
  = -- | An error occurred while executing a statement in the session.
    --
    -- This wraps statement-level errors and provides additional context about:
    --
    -- * Which statement in the pipeline failed (when multiple statements are batched)
    -- * The SQL text and parameters of the failing statement
    -- * Whether the statement was prepared or unprepared
    --
    -- The error message includes formatted output showing all this context,
    -- making it easier to diagnose issues in production.
    StatementSessionError
      -- | Total number of statements in the running pipeline. 1 if it's executed alone.
      Int
      -- | 0-based index of the statement that failed. 0 if it's executed alone.
      Int
      -- | SQL template of the failing statement.
      Text
      -- | Parameter values as text (for logging purposes).
      [Text]
      -- | Whether the statement was executed as a prepared one.
      Bool
      -- | The underlying statement error.
      StatementError
  | -- | An error occurred while executing a script.
    --
    -- Scripts are multi-statement SQL texts executed via 'Hasql.Session.script'.
    -- Unlike regular statements, scripts don't support parameters or result decoding,
    -- and errors are limited to server-reported issues.
    ScriptSessionError
      -- | The SQL text of the script.
      Text
      -- | The server error that occurred.
      ServerError
  | -- | A connection-level error occurred during session execution.
    --
    -- This indicates that the connection to the database was lost or
    -- became unusable during the session. These errors are transient
    -- and the operation can be retried with a new connection.
    --
    -- Note: As of version 1.10, connections recover from async exceptions
    -- without resetting, preserving connection-local state.
    ConnectionSessionError
      -- | Human-readable details about the connection error.
      Text
  | -- | One or more types referenced in the statement could not be found in the database.
    --
    -- This occurs when using custom types (enums, composite types, domains) that
    -- are resolved by name at runtime, but the types don't exist in the database.
    --
    -- To fix this error:
    --
    -- * Ensure the types are defined in the database
    -- * Check that schema search paths are configured correctly
    -- * Verify that the type names in your code match those in the database
    MissingTypesSessionError
      -- | Set of (schema name, type name) pairs that could not be found.
      --
      -- Schema name is 'Nothing' when the type was looked up without a schema qualifier.
      (HashSet (Maybe Text, Text))
  | -- | An internal driver error occurred.
    --
    -- This indicates either:
    --
    -- * A bug in Hasql
    -- * The PostgreSQL server misbehaving
    -- * An unexpected response from the server
    --
    -- If you encounter this error, please report it as a bug.
    DriverSessionError
      -- | Human-readable details about what went wrong.
      Text
  deriving stock (Int -> SessionError -> ShowS
[SessionError] -> ShowS
SessionError -> String
(Int -> SessionError -> ShowS)
-> (SessionError -> String)
-> ([SessionError] -> ShowS)
-> Show SessionError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SessionError -> ShowS
showsPrec :: Int -> SessionError -> ShowS
$cshow :: SessionError -> String
show :: SessionError -> String
$cshowList :: [SessionError] -> ShowS
showList :: [SessionError] -> ShowS
Show, SessionError -> SessionError -> Bool
(SessionError -> SessionError -> Bool)
-> (SessionError -> SessionError -> Bool) -> Eq SessionError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SessionError -> SessionError -> Bool
== :: SessionError -> SessionError -> Bool
$c/= :: SessionError -> SessionError -> Bool
/= :: SessionError -> SessionError -> Bool
Eq)

-- |
-- Error that occurs when executing a single statement.
--
-- Statement errors can be caused by server-side issues (SQL errors, constraint violations)
-- or by mismatches between the decoder specification and the actual result structure
-- (wrong number of rows/columns, type mismatches, or cell-level decoding failures).
data StatementError
  = -- | The server rejected the statement and returned an error.
    --
    -- This includes SQL syntax errors, constraint violations, permission errors,
    -- and any other error reported by PostgreSQL during statement execution.
    ServerStatementError ServerError
  | -- | The statement returned a different number of rows than expected.
    --
    -- This occurs when using result decoders like @Decoders.singleRow@ or
    -- @Decoders.rowsAffectedAtLeast@ that have specific row count expectations.
    UnexpectedRowCountStatementError
      -- | Expected minimum number of rows.
      Int
      -- | Expected maximum number of rows.
      Int
      -- | Actual number of rows returned.
      Int
  | -- | The statement returned a different number of columns than expected.
    --
    -- This indicates a mismatch between the decoder specification and the
    -- actual result structure, possibly due to:
    --
    -- * Schema changes (columns added or removed)
    -- * Wrong query (selecting different columns than expected)
    -- * Decoder configuration error
    UnexpectedColumnCountStatementError
      -- | Expected number of columns.
      Int
      -- | Actual number of columns returned.
      Int
  | -- | A column has a different type than expected.
    --
    -- This occurs when the decoder expects a specific PostgreSQL type (by OID)
    -- but the actual column has a different type. This can happen due to:
    --
    -- * Schema changes (column type changed)
    -- * Wrong query (selecting wrong column or using a cast)
    -- * Decoder configuration error
    --
    -- Note: As of version 1.10, Hasql performs strict type checking and will
    -- report this error instead of attempting automatic type coercion.
    UnexpectedColumnTypeStatementError
      -- | 0-based column index where the type mismatch occurred.
      Int
      -- | Expected PostgreSQL type OID.
      Word32
      -- | Actual PostgreSQL type OID of the column.
      Word32
  | -- | An error occurred while decoding a specific row.
    --
    -- This wraps errors that occur at the row or cell level, providing
    -- context about which row failed.
    RowStatementError
      -- | 0-based index of the row that failed to decode.
      Int
      -- | The underlying row-level error.
      RowError
  | -- | The database returned an unexpected result structure.
    --
    -- This is a catch-all error that indicates either:
    --
    -- * An improper statement (e.g., executing a query that doesn't match expectations)
    -- * A schema mismatch between the code and database
    -- * A bug in Hasql or server misbehavior
    UnexpectedResultStatementError
      -- | Human-readable details about what went wrong.
      Text
  deriving stock (Int -> StatementError -> ShowS
[StatementError] -> ShowS
StatementError -> String
(Int -> StatementError -> ShowS)
-> (StatementError -> String)
-> ([StatementError] -> ShowS)
-> Show StatementError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> StatementError -> ShowS
showsPrec :: Int -> StatementError -> ShowS
$cshow :: StatementError -> String
show :: StatementError -> String
$cshowList :: [StatementError] -> ShowS
showList :: [StatementError] -> ShowS
Show, StatementError -> StatementError -> Bool
(StatementError -> StatementError -> Bool)
-> (StatementError -> StatementError -> Bool) -> Eq StatementError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: StatementError -> StatementError -> Bool
== :: StatementError -> StatementError -> Bool
$c/= :: StatementError -> StatementError -> Bool
/= :: StatementError -> StatementError -> Bool
Eq)

-- |
-- Error reported by the PostgreSQL server when executing a statement.
--
-- The server provides structured error information including error codes
-- (SQL state), messages, and optional context like hints and position information.
--
-- For a complete list of PostgreSQL error codes, see:
-- <https://www.postgresql.org/docs/current/errcodes-appendix.html>
data ServerError
  = ServerError
      -- | SQL State Code (SQLSTATE).
      --
      -- A five-character code that identifies the error class and condition.
      -- Examples: @\"23505\"@ (unique violation), @\"42P01\"@ (undefined table).
      Text
      -- | Primary error message.
      --
      -- A human-readable description of the error.
      Text
      -- | Optional detailed error information.
      --
      -- Additional context about the error, if available.
      (Maybe Text)
      -- | Optional hint for resolving the error.
      --
      -- A suggestion for how to fix the problem, if available.
      (Maybe Text)
      -- | Optional position in the SQL string where the error occurred.
      --
      -- A 1-based character index into the SQL string, if the error
      -- relates to a specific location in the query.
      (Maybe Int)
  deriving stock (Int -> ServerError -> ShowS
[ServerError] -> ShowS
ServerError -> String
(Int -> ServerError -> ShowS)
-> (ServerError -> String)
-> ([ServerError] -> ShowS)
-> Show ServerError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ServerError -> ShowS
showsPrec :: Int -> ServerError -> ShowS
$cshow :: ServerError -> String
show :: ServerError -> String
$cshowList :: [ServerError] -> ShowS
showList :: [ServerError] -> ShowS
Show, ServerError -> ServerError -> Bool
(ServerError -> ServerError -> Bool)
-> (ServerError -> ServerError -> Bool) -> Eq ServerError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ServerError -> ServerError -> Bool
== :: ServerError -> ServerError -> Bool
$c/= :: ServerError -> ServerError -> Bool
/= :: ServerError -> ServerError -> Bool
Eq)

-- |
-- Error that occurs when decoding a result row.
--
-- Row errors indicate problems when processing an individual row from the result set,
-- either at the cell level or during row refinement/validation.
data RowError
  = -- | An error occurred while decoding a specific cell in the row.
    --
    -- This wraps cell-level errors (null handling, deserialization failures)
    -- and provides context about which column failed and its type.
    CellRowError
      -- | 0-based index of the column where the error occurred.
      Int
      -- | PostgreSQL type OID of the column, as reported by the server.
      Word32
      -- | The underlying cell-level error.
      CellError
  | -- | A refinement or validation error when processing the row.
    --
    -- This occurs when using refinement functions in row decoders
    -- (e.g., with 'Decoders.refineRow') to validate or transform
    -- decoded values. The refinement function rejected the row data.
    RefinementRowError
      -- | Human-readable details about why the refinement failed.
      Text
  deriving stock (Int -> RowError -> ShowS
[RowError] -> ShowS
RowError -> String
(Int -> RowError -> ShowS)
-> (RowError -> String) -> ([RowError] -> ShowS) -> Show RowError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RowError -> ShowS
showsPrec :: Int -> RowError -> ShowS
$cshow :: RowError -> String
show :: RowError -> String
$cshowList :: [RowError] -> ShowS
showList :: [RowError] -> ShowS
Show, RowError -> RowError -> Bool
(RowError -> RowError -> Bool)
-> (RowError -> RowError -> Bool) -> Eq RowError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RowError -> RowError -> Bool
== :: RowError -> RowError -> Bool
$c/= :: RowError -> RowError -> Bool
/= :: RowError -> RowError -> Bool
Eq)

-- |
-- Error that occurs when decoding a single cell (column value) in a result row.
--
-- Cell errors indicate problems with individual values returned by the database,
-- such as unexpected nulls or failures in binary deserialization.
data CellError
  = -- | A NULL value was encountered when a non-NULL value was expected.
    --
    -- This occurs when using non-nullable decoders (e.g., @Decoders.nonNullable@)
    -- on a column that contains a NULL value.
    UnexpectedNullCellError
  | -- | Failed to deserialize the cell value from its binary representation.
    --
    -- This can occur when:
    --
    -- * The binary data is corrupted
    -- * The decoder doesn't match the actual data type
    -- * The data format is invalid for the expected type
    DeserializationCellError
      -- | Human-readable error message describing what went wrong.
      Text
  deriving stock (Int -> CellError -> ShowS
[CellError] -> ShowS
CellError -> String
(Int -> CellError -> ShowS)
-> (CellError -> String)
-> ([CellError] -> ShowS)
-> Show CellError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CellError -> ShowS
showsPrec :: Int -> CellError -> ShowS
$cshow :: CellError -> String
show :: CellError -> String
$cshowList :: [CellError] -> ShowS
showList :: [CellError] -> ShowS
Show, CellError -> CellError -> Bool
(CellError -> CellError -> Bool)
-> (CellError -> CellError -> Bool) -> Eq CellError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CellError -> CellError -> Bool
== :: CellError -> CellError -> Bool
$c/= :: CellError -> CellError -> Bool
/= :: CellError -> CellError -> Bool
Eq)

fromRoundtripError :: Hasql.Comms.Roundtrip.Error context -> SessionError
fromRoundtripError :: forall context. Error context -> SessionError
fromRoundtripError = \case
  Hasql.Comms.Roundtrip.ClientError context
_context Maybe ByteString
details ->
    Text -> SessionError
ConnectionSessionError (Text -> (ByteString -> Text) -> Maybe ByteString -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" ByteString -> Text
decodeUtf8Lenient Maybe ByteString
details)
  Hasql.Comms.Roundtrip.ServerError Error context
recvError ->
    Error (Maybe (Int, Int, ByteString, [Text], Bool)) -> SessionError
fromRecvError (Maybe (Int, Int, ByteString, [Text], Bool)
forall a. Maybe a
Nothing Maybe (Int, Int, ByteString, [Text], Bool)
-> Error context
-> Error (Maybe (Int, Int, ByteString, [Text], Bool))
forall a b. a -> Error b -> Error a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Error context
recvError)

fromRecvError :: Hasql.Comms.Recv.Error (Maybe (Int, Int, ByteString, [Text], Bool)) -> SessionError
fromRecvError :: Error (Maybe (Int, Int, ByteString, [Text], Bool)) -> SessionError
fromRecvError = \case
  Hasql.Comms.Recv.ResultError Maybe (Int, Int, ByteString, [Text], Bool)
location Int
_resultOffset Error
resultError ->
    case Maybe (Int, Int, ByteString, [Text], Bool)
location of
      Maybe (Int, Int, ByteString, [Text], Bool)
Nothing ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Unexpected error outside of statement context. ",
            TextBuilder
"This indicates a bug in Hasql or the server misbehaving. ",
            TextBuilder
"Error: ",
            String -> TextBuilder
TextBuilder.string (Error -> String
forall a. Show a => a -> String
show Error
resultError)
          ]
      Just (Int
totalStatements, Int
statementIndex, ByteString
sql, [Text]
parameters, Bool
prepared) ->
        Int
-> Int -> Text -> [Text] -> Bool -> StatementError -> SessionError
StatementSessionError
          Int
totalStatements
          Int
statementIndex
          (ByteString -> Text
decodeUtf8Lenient ByteString
sql)
          [Text]
parameters
          Bool
prepared
          (Error -> StatementError
fromStatementResultError Error
resultError)
  Hasql.Comms.Recv.NoResultsError Maybe (Int, Int, ByteString, [Text], Bool)
location Maybe ByteString
details ->
    case Maybe (Int, Int, ByteString, [Text], Bool)
location of
      Maybe (Int, Int, ByteString, [Text], Bool)
Nothing ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Unexpectedly got no results outside of statement context. ",
            TextBuilder
"This indicates a bug in Hasql or the server misbehaving. ",
            TextBuilder
"Details: ",
            String -> TextBuilder
TextBuilder.string (Maybe ByteString -> String
forall a. Show a => a -> String
show Maybe ByteString
details)
          ]
      Just (Int
totalStatements, Int
statementIndex, ByteString
sql, [Text]
parameters, Bool
prepared) ->
        Int
-> Int -> Text -> [Text] -> Bool -> StatementError -> SessionError
StatementSessionError
          Int
totalStatements
          Int
statementIndex
          (ByteString -> Text
decodeUtf8Lenient ByteString
sql)
          [Text]
parameters
          Bool
prepared
          (Int -> Int -> Int -> StatementError
UnexpectedRowCountStatementError Int
1 Int
1 Int
0)
  Hasql.Comms.Recv.TooManyResultsError Maybe (Int, Int, ByteString, [Text], Bool)
location Int
actual ->
    case Maybe (Int, Int, ByteString, [Text], Bool)
location of
      Maybe (Int, Int, ByteString, [Text], Bool)
Nothing ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Unexpectedly got too many results outside of statement context. ",
            TextBuilder
"This indicates a bug in Hasql or the server misbehaving. ",
            TextBuilder
"Amount: ",
            Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
actual
          ]
      Just (Int
totalStatements, Int
statementIndex, ByteString
sql, [Text]
parameters, Bool
prepared) ->
        Int
-> Int -> Text -> [Text] -> Bool -> StatementError -> SessionError
StatementSessionError
          Int
totalStatements
          Int
statementIndex
          (ByteString -> Text
decodeUtf8Lenient ByteString
sql)
          [Text]
parameters
          Bool
prepared
          (Int -> Int -> Int -> StatementError
UnexpectedRowCountStatementError Int
1 Int
1 Int
actual)

fromStatementResultError :: Hasql.Comms.ResultDecoder.Error -> StatementError
fromStatementResultError :: Error -> StatementError
fromStatementResultError = \case
  Hasql.Comms.ResultDecoder.ServerError ByteString
code ByteString
message Maybe ByteString
detail Maybe ByteString
hint Maybe Int
position ->
    ServerError -> StatementError
ServerStatementError
      ( Text
-> Text -> Maybe Text -> Maybe Text -> Maybe Int -> ServerError
ServerError
          (ByteString -> Text
decodeUtf8Lenient ByteString
code)
          (ByteString -> Text
decodeUtf8Lenient ByteString
message)
          ((ByteString -> Text) -> Maybe ByteString -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
decodeUtf8Lenient Maybe ByteString
detail)
          ((ByteString -> Text) -> Maybe ByteString -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
decodeUtf8Lenient Maybe ByteString
hint)
          Maybe Int
position
      )
  Hasql.Comms.ResultDecoder.UnexpectedResult Text
msg ->
    Text -> StatementError
UnexpectedResultStatementError Text
msg
  Hasql.Comms.ResultDecoder.UnexpectedRowCount Int
actual ->
    Int -> Int -> Int -> StatementError
UnexpectedRowCountStatementError Int
1 Int
1 Int
actual
  Hasql.Comms.ResultDecoder.UnexpectedColumnCount Int
expected Int
actual ->
    Int -> Int -> StatementError
UnexpectedColumnCountStatementError Int
expected Int
actual
  Hasql.Comms.ResultDecoder.DecoderTypeMismatch Int
colIdx Word32
expectedOid Word32
actualOid ->
    Int -> Word32 -> Word32 -> StatementError
UnexpectedColumnTypeStatementError
      Int
colIdx
      Word32
expectedOid
      Word32
actualOid
  Hasql.Comms.ResultDecoder.RowError Int
rowIndex Error
rowError ->
    case Error
rowError of
      Hasql.Comms.RowReader.CellError Int
column Word32
oid CellError
cellErr ->
        Int -> RowError -> StatementError
RowStatementError
          Int
rowIndex
          ( Int -> Word32 -> CellError -> RowError
CellRowError
              Int
column
              Word32
oid
              ( case CellError
cellErr of
                  Hasql.Comms.RowReader.DecodingCellError Text
msg -> Text -> CellError
DeserializationCellError Text
msg
                  CellError
Hasql.Comms.RowReader.UnexpectedNullCellError -> CellError
UnexpectedNullCellError
              )
          )
      Hasql.Comms.RowReader.RefinementError Text
msg ->
        Int -> RowError -> StatementError
RowStatementError
          Int
rowIndex
          (Text -> RowError
RefinementRowError Text
msg)

fromRecvErrorInScript :: ByteString -> Hasql.Comms.Recv.Error (Maybe ByteString) -> SessionError
fromRecvErrorInScript :: ByteString -> Error (Maybe ByteString) -> SessionError
fromRecvErrorInScript ByteString
scriptSql = \case
  Hasql.Comms.Recv.ResultError Maybe ByteString
_ Int
_ Error
resultError ->
    case Error
resultError of
      Hasql.Comms.ResultDecoder.ServerError ByteString
code ByteString
message Maybe ByteString
detail Maybe ByteString
hint Maybe Int
position ->
        Text -> ServerError -> SessionError
ScriptSessionError
          (ByteString -> Text
decodeUtf8Lenient ByteString
scriptSql)
          ( Text
-> Text -> Maybe Text -> Maybe Text -> Maybe Int -> ServerError
ServerError
              (ByteString -> Text
decodeUtf8Lenient ByteString
code)
              (ByteString -> Text
decodeUtf8Lenient ByteString
message)
              ((ByteString -> Text) -> Maybe ByteString -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
decodeUtf8Lenient Maybe ByteString
detail)
              ((ByteString -> Text) -> Maybe ByteString -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
decodeUtf8Lenient Maybe ByteString
hint)
              Maybe Int
position
          )
      Hasql.Comms.ResultDecoder.UnexpectedResult Text
msg ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Unexpected result in script: ",
            Text -> TextBuilder
TextBuilder.text Text
msg
          ]
      Hasql.Comms.ResultDecoder.UnexpectedRowCount Int
actual ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Unexpected amount of rows in script: ",
            Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
actual
          ]
      Hasql.Comms.ResultDecoder.UnexpectedColumnCount Int
expected Int
actual ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Unexpected amount of columns in script: expected ",
            Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
expected,
            TextBuilder
", got ",
            Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
actual
          ]
      Hasql.Comms.ResultDecoder.DecoderTypeMismatch Int
colIdx Word32
expectedOid Word32
actualOid ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Decoder type mismatch in script: expected OID ",
            String -> TextBuilder
TextBuilder.string (Word32 -> String
forall a. Show a => a -> String
show Word32
expectedOid),
            TextBuilder
" at column ",
            Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
colIdx,
            TextBuilder
", got ",
            String -> TextBuilder
TextBuilder.string (Word32 -> String
forall a. Show a => a -> String
show Word32
actualOid)
          ]
      Hasql.Comms.ResultDecoder.RowError Int
rowIndex Error
rowError ->
        (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
          [ TextBuilder
"Row error in script at row ",
            Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
rowIndex,
            TextBuilder
": ",
            String -> TextBuilder
TextBuilder.string (Error -> String
forall a. Show a => a -> String
show Error
rowError)
          ]
  Hasql.Comms.Recv.NoResultsError Maybe ByteString
_ Maybe ByteString
details ->
    (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
      [ TextBuilder
"Got no results in script.",
        TextBuilder
" This indicates a bug in Hasql or the server misbehaving.",
        Maybe ByteString
details
          Maybe ByteString
-> (Maybe ByteString -> Maybe ByteString) -> Maybe ByteString
forall a b. a -> (a -> b) -> b
& (ByteString -> Bool) -> Maybe ByteString -> Maybe ByteString
forall a. (a -> Bool) -> Maybe a -> Maybe a
forall (f :: * -> *) a. Filterable f => (a -> Bool) -> f a -> f a
filter (ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
/= ByteString
"")
          Maybe ByteString
-> (Maybe ByteString -> TextBuilder) -> TextBuilder
forall a b. a -> (a -> b) -> b
& (ByteString -> TextBuilder) -> Maybe ByteString -> TextBuilder
forall m a. Monoid m => (a -> m) -> Maybe a -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (TextBuilder -> TextBuilder -> TextBuilder
forall a. Monoid a => a -> a -> a
mappend TextBuilder
" Details: " (TextBuilder -> TextBuilder)
-> (ByteString -> TextBuilder) -> ByteString -> TextBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Text -> TextBuilder
TextBuilder.text (Text -> TextBuilder)
-> (ByteString -> Text) -> ByteString -> TextBuilder
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Text
decodeUtf8Lenient)
      ]
  Hasql.Comms.Recv.TooManyResultsError Maybe ByteString
_ Int
actual ->
    (Text -> SessionError
DriverSessionError (Text -> SessionError)
-> ([TextBuilder] -> Text) -> [TextBuilder] -> SessionError
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
      [ TextBuilder
"Got too many results in script. ",
        TextBuilder
"This indicates a bug in Hasql or the server misbehaving. ",
        TextBuilder
"Amount: ",
        Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
actual
      ]