diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,14 @@
 # Changelog
 
+## 0.5.1.0
+
+`hasql-check-hie` identified an inferred quote by its `Params` dictionary,
+which the quoters that build their own encoder and decoder do not carry, so
+it emitted nothing for them; the plugin matched only `statement` and
+`resultless` and skipped them too. Both now read the Haskell types from
+`HaskellParams` and `HaskellColumns`, and evaluate the `Append` that GHC
+records unreduced.
+
 ## 0.5.0.0
 
 Initial release, alongside `hasql-check` 0.5.0.0: `Hasql.Check.Plugin`, a GHC
diff --git a/hasql-check-ghc.cabal b/hasql-check-ghc.cabal
--- a/hasql-check-ghc.cabal
+++ b/hasql-check-ghc.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.0
 name:            hasql-check-ghc
-version:         0.5.0.0
+version:         0.5.1.0
 synopsis:
   Check inferred hasql statements with what GHC knows of their types
 
diff --git a/src/Hasql/Check/Ghc/Source.hs b/src/Hasql/Check/Ghc/Source.hs
--- a/src/Hasql/Check/Ghc/Source.hs
+++ b/src/Hasql/Check/Ghc/Source.hs
@@ -1,19 +1,20 @@
--- |
--- Module      : Hasql.Check.Ghc.Source
--- Copyright   : (c) 2026 Kronor.io
---
--- License     : BSD-3-Clause
--- Maintainer  : lorenzo@kronor.io
--- Stability   : experimental
--- Portability : GHC
---
--- A statement of "Hasql.Check.Inferred.TH", from what GHC knows of it: its
--- quote, found in its source from the span GHC gives the code it generated,
--- and its types, from the dictionaries that code is passed. Whether GHC's
--- types come from a @.hie@ file or a compiler plugin, they are read here.
---
--- GHC counts columns from 1, and a tab as reaching the next tab stop, every 8
--- columns; the source text counts characters.
+{- |
+Module      : Hasql.Check.Ghc.Source
+Copyright   : (c) 2026 Kronor.io
+
+License     : BSD-3-Clause
+Maintainer  : lorenzo@kronor.io
+Stability   : experimental
+Portability : GHC
+
+A statement of "Hasql.Check.Inferred.TH", from what GHC knows of it: its
+quote, found in its source from the span GHC gives the code it generated,
+and its types, from the dictionaries that code is passed. Whether GHC's
+types come from a @.hie@ file or a compiler plugin, they are read here.
+
+GHC counts columns from 1, and a tab as reaching the next tab stop, every 8
+columns; the source text counts characters.
+-}
 module Hasql.Check.Ghc.Source (
     -- * Statements
     Statement (..),
@@ -39,7 +40,6 @@
 import Data.Char (isAlphaNum, isDigit)
 import Data.List (intercalate)
 
-
 data Quote = Quote
     { column :: Int
     -- ^ GHC's column of the quote's opening bracket.
@@ -48,10 +48,10 @@
     }
     deriving stock (Eq, Show)
 
-
--- | The quote a span covers, given the source's lines and the span's start
--- and end, as GHC's line and column. The span starts right after
--- @[quoter|@ and ends right after @|]@; any other span is not a quote's.
+{- | The quote a span covers, given the source's lines and the span's start
+and end, as GHC's line and column. The span starts right after
+@[quoter|@ and ends right after @|]@; any other span is not a quote's.
+-}
 quoteAt :: [String] -> (Int, Int) -> (Int, Int) -> Maybe Quote
 quoteAt source (startLine, startColumn) (endLine, endColumn) = do
     first <- line startLine
@@ -89,21 +89,21 @@
         [] -> -1
         is -> last is
 
-
 -- | What the dictionaries in a quote's span say about its statement's types.
 data Types t
     = -- | Not a statement of Hasql.Check.Inferred.TH.
       NotInferred
-    | -- | Its types are only known where it is used, and may differ from one
-      -- use to the next.
+    | {- | Its types are only known where it is used, and may differ from one
+      use to the next.
+      -}
       Unresolved
     | -- | Its parameter type, and its row type unless it returns no rows.
       Resolved t (Maybe t)
     deriving stock (Eq, Show)
 
-
--- | The statement's types, given which types are concrete, and the types of
--- the quote's Params and Row dictionaries.
+{- | The statement's types, given which types are concrete, and the types of
+the quote's Params and Row dictionaries.
+-}
 typesOf :: (t -> Bool) -> [t] -> [t] -> Types t
 typesOf concrete params rows = case (filter concrete params, filter concrete rows) of
     _ | null params -> NotInferred
@@ -111,7 +111,6 @@
     (_, []) | not (null rows) -> Unresolved
     (p : _, r) -> Resolved p (case r of x : _ -> Just x; [] -> Nothing)
 
-
 -- | The index in a line of the character at a GHC column.
 charIndex :: String -> Int -> Int
 charIndex = go 0 1
@@ -121,25 +120,22 @@
         | x : xs <- rest = go (i + 1) (advance c x) xs target
         | otherwise = i
 
-
 -- | GHC's column of the character at an index in a line.
 ghcColumn :: String -> Int -> Int
 ghcColumn s i = foldl advance 1 (take i s)
 
-
 advance :: Int -> Char -> Int
 advance c = \case
     '\t' -> ((c - 1) `div` 8 + 1) * 8 + 1
     _ -> c + 1
 
-
--- | A type: a constructor applied to its visible arguments, by module and
--- name; a string literal, which names a Postgres type or label; another
--- literal; or something not concrete.
+{- | A type: a constructor applied to its visible arguments, by module and
+name; a string literal, which names a Postgres type or label; another
+literal; or something not concrete.
+-}
 data Ty = Con String String [Ty] | Lit String | OtherLit String | Var
     deriving stock (Eq, Show)
 
-
 -- | Whether a type is concrete.
 ground :: Ty -> Bool
 ground = \case
@@ -148,9 +144,9 @@
     OtherLit _ -> True
     Con _ _ args -> all ground args
 
-
--- | A parameter or row type's columns, each with whether it is a 'Maybe':
--- a tuple's elements, none for unit as parameters, or the type itself.
+{- | A parameter or row type's columns, each with whether it is a 'Maybe':
+a tuple's elements, none for unit as parameters, or the type itself.
+-}
 columnTypes :: Bool -> Ty -> [(Ty, Bool)]
 columnTypes isParams t = map column (split t)
   where
@@ -158,27 +154,28 @@
         Con m "Unit" [] | isParams, m `elem` tupleModules -> []
         Con m name args | m `elem` tupleModules, isTuple name -> args
         other -> [other]
-    column = \case
-        Con m "Maybe" [inner] | m `elem` maybeModules -> (inner, True)
-        other -> (other, False)
+    column = columnOf
 
+-- | A column's type and whether it is a 'Maybe', so may be NULL.
+columnOf :: Ty -> (Ty, Bool)
+columnOf = \case
+    Con m "Maybe" [inner] | m `elem` maybeModules -> (inner, True)
+    other -> (other, False)
 
--- | Where GHC defines tuples and unit, and Maybe: a type of the same name
--- elsewhere, a newtype TupleId say, is a type of its own.
+{- | Where GHC defines tuples and unit, and Maybe: a type of the same name
+elsewhere, a newtype TupleId say, is a type of its own.
+-}
 tupleModules, maybeModules :: [String]
 tupleModules = ["GHC.Tuple", "GHC.Tuple.Prim"]
 maybeModules = ["GHC.Internal.Maybe", "GHC.Maybe"]
 
-
 isTuple :: String -> Bool
 isTuple name = case splitAt 5 name of
     ("Tuple", n) -> not (null n) && all isDigit n
     _ -> False
 
-
 -- * Statements
 
-
 -- | A statement, with what the checker needs of it.
 data Statement = Statement
     { line :: Int
@@ -188,7 +185,6 @@
     -- ^ Nothing for a statement that returns no rows.
     }
 
-
 -- | A parameter or result column.
 data Column = Column
     { haskellType :: String
@@ -198,37 +194,54 @@
     , nullable :: Bool
     }
 
-
--- | The statement whose generated code has the given span, given the source
--- lines and the types of the dictionaries the code is passed there: nothing
--- if the span is not a quote of these quoters, an error if its types are not
--- concrete there.
+{- | The statement whose generated code has the given span, given the source
+lines and the types of the dictionaries the code is passed there: nothing
+if the span is not a quote of these quoters, an error if its types are not
+concrete there.
+-}
 statementAt :: [String] -> (Int, Int) -> (Int, Int) -> [Ty] -> Maybe (Either String Statement)
 statementAt source start@(line', _) end here = do
     q <- quoteAt source start end
     statementOf line' q here
 
-
--- | The statement of a quote on a line, given the types of the dictionaries
--- its generated code is passed: nothing if it is not a quote of these
--- quoters, an error if its types are not concrete there.
+{- | The statement of a quote on a line, given the types of the dictionaries
+its generated code is passed: nothing if it is not a quote of these
+quoters, an error if its types are not concrete there.
+-}
 statementOf :: Int -> Quote -> [Ty] -> Maybe (Either String Statement)
-statementOf line' q here =
-    case typesOf ground params' rows of
+statementOf line' q here = case first paramHs of
+    -- The quoters build the encoder and the decoder themselves, so the types
+    -- come one per placeholder and per column rather than as a tuple to
+    -- split: a record target and a param field are neither.
+    Just hs
+        | Just names <- first paramNames
+        , Just labels' <- first paramLabels ->
+            Just (Right (Statement line' q (columnsOf (map columnOf hs) names labels') resultRow))
+        | otherwise -> Just (Left unresolved)
+    Nothing -> case typesOf ground params' rows of
         NotInferred -> Nothing
         Resolved p r
             | Just names <- first paramNames
             , Just labels' <- first paramLabels
             , Just row' <- traverse (\r' -> (r',,) <$> first columnNames <*> first columnLabels) r ->
                 Just (Right (Statement line' q (columns True (p, names, labels')) (columns False <$> row')))
-        _ ->
-            Just
-                ( Left
-                    "the statement's types are not known where it is written, so it cannot be checked; give its binding a concrete type, such as Statement Int64 Text"
-                )
+        _ -> Just (Left unresolved)
   where
+    unresolved =
+        "the statement's types are not known where it is written, so it cannot be checked; give its binding a concrete type, such as Statement Int64 Text"
+    -- A statement that returns rows carries either the column types the
+    -- quoter collected, or, where it could not count them, the Haskell row
+    -- type's. One that returns none carries neither.
+    resultRow = case (first columnNames, first columnLabels) of
+        (Just names, Just labels') -> case (first columnHs, filter ground rows) of
+            (Just hs, _) -> Just (columnsOf (map columnOf hs) names labels')
+            (Nothing, r : _) -> Just (columnsOf (columnTypes False r) names labels')
+            _ -> Nothing
+        _ -> Nothing
     params' = [p | Con "Hasql.Check.Inferred" "Params" [p] <- here]
     rows = [r | Con "Hasql.Check.Inferred" "Row" [r] <- here]
+    paramHs = [hs | Con "Hasql.Check.Inferred" "HaskellParams" [l] <- here, Just hs <- [types l], all ground hs]
+    columnHs = [hs | Con "Hasql.Check.Inferred" "HaskellColumns" [l] <- here, Just hs <- [types l], all ground hs]
     paramNames = [ns | Con "Hasql.Check.Inferred" "PostgresParams" [l] <- here, Just ns <- [symbols l]]
     columnNames = [ns | Con "Hasql.Check.Inferred" "PostgresColumns" [l] <- here, Just ns <- [symbols l]]
     paramLabels = [ls | Con "Hasql.Check.Inferred" "PostgresParamLabels" [l] <- here, Just ls <- [labelLists l]]
@@ -236,9 +249,9 @@
     first = \case
         x : _ -> Just x
         [] -> Nothing
-    columns isParams (t, names, labels') =
-        zipWith3 (\(c, nullable') postgres' ls -> Column (typeName c) postgres' ls nullable') (columnTypes isParams t) names labels'
-
+    columns isParams (t, names, labels') = columnsOf (columnTypes isParams t) names labels'
+    columnsOf cs names labels' =
+        zipWith3 (\(c, nullable') postgres' ls -> Column (typeName c) postgres' ls nullable') cs names labels'
 
 -- | A type as Haskell would write it, qualified.
 typeName :: Ty -> String
@@ -250,18 +263,31 @@
   where
     parenthesised name = if ' ' `elem` name then concat ["(", name, ")"] else name
 
-
--- | A type-level list of strings.
+{- | A type-level list of strings. The quoters build theirs by appending,
+and GHC records the Append unreduced, so it is evaluated here.
+-}
 symbols :: Ty -> Maybe [String]
 symbols = \case
     Con _ ":" [Lit s, rest] -> (s :) <$> symbols rest
     Con _ "[]" [] -> Just []
+    Con _ "Append" [a, b] -> (<>) <$> symbols a <*> symbols b
     _ -> Nothing
 
+{- | A type-level list of types, as 'Slots' and 'Target' carry: one per
+placeholder or result column, which a record target or a param field makes
+something other than a tuple's elements.
+-}
+types :: Ty -> Maybe [Ty]
+types = \case
+    Con _ ":" [t, rest] -> (t :) <$> types rest
+    Con _ "[]" [] -> Just []
+    Con _ "Append" [a, b] -> (<>) <$> types a <*> types b
+    _ -> Nothing
 
 -- | A type-level list of lists of strings.
 labelLists :: Ty -> Maybe [[String]]
 labelLists = \case
     Con _ ":" [l, rest] -> (:) <$> symbols l <*> labelLists rest
     Con _ "[]" [] -> Just []
+    Con _ "Append" [a, b] -> (<>) <$> labelLists a <*> labelLists b
     _ -> Nothing
diff --git a/src/Hasql/Check/Plugin.hs b/src/Hasql/Check/Plugin.hs
--- a/src/Hasql/Check/Plugin.hs
+++ b/src/Hasql/Check/Plugin.hs
@@ -1,25 +1,26 @@
--- |
--- Module      : Hasql.Check.Plugin
--- Copyright   : (c) 2026 Kronor.io
---
--- License     : BSD-3-Clause
--- Maintainer  : lorenzo@kronor.io
--- Stability   : experimental
--- Portability : GHC
---
--- A GHC plugin that checks the statements of "Hasql.Check.Inferred.TH"
--- against a database while their module compiles, as "Hasql.Check.TH" does
--- for hasql-th's: once GHC has type-checked the module, the types each
--- statement is used at are known, and the plugin checks it as
--- @hasql-check --inferred@ would. A statement that does not fit the schema
--- is a compile error at its quote.
---
--- > {-# OPTIONS_GHC -fplugin=Hasql.Check.Plugin #-}
---
--- or @ghc-options: -fplugin=Hasql.Check.Plugin@ for a whole package, which
--- depends on @hasql-check-ghc@. It is configured as "Hasql.Check.TH" is, by
--- @HASQL_CHECK_DATABASE_URL@ and the rest (see "Hasql.Check.Compile"); with
--- no database set, it checks nothing.
+{- |
+Module      : Hasql.Check.Plugin
+Copyright   : (c) 2026 Kronor.io
+
+License     : BSD-3-Clause
+Maintainer  : lorenzo@kronor.io
+Stability   : experimental
+Portability : GHC
+
+A GHC plugin that checks the statements of "Hasql.Check.Inferred.TH"
+against a database while their module compiles, as "Hasql.Check.TH" does
+for hasql-th's: once GHC has type-checked the module, the types each
+statement is used at are known, and the plugin checks it as
+@hasql-check --inferred@ would. A statement that does not fit the schema
+is a compile error at its quote.
+
+> {\-# OPTIONS_GHC -fplugin=Hasql.Check.Plugin #-\}
+
+or @ghc-options: -fplugin=Hasql.Check.Plugin@ for a whole package, which
+depends on @hasql-check-ghc@. It is configured as "Hasql.Check.TH" is, by
+@HASQL_CHECK_DATABASE_URL@ and the rest (see "Hasql.Check.Compile"); with
+no database set, it checks nothing.
+-}
 module Hasql.Check.Plugin (plugin) where
 
 import Control.Monad (forM_, unless)
@@ -51,7 +52,6 @@
 import Hasql.Check.Inferred.Quote (Inferred (Inferred), checkInferred)
 import Hasql.Check.Inferred.Quote qualified as Quote
 
-
 plugin :: Plugin
 plugin =
     defaultPlugin
@@ -61,15 +61,14 @@
         , pluginRecompile = \_ -> recompile <$> readConfig
         }
 
-
--- | A module is checked again when what it was checked with changes, as a
--- different database; a change of schema rechecks only the modules with
--- statements, which depend on the schema file.
+{- | A module is checked again when what it was checked with changes, as a
+different database; a change of schema rechecks only the modules with
+statements, which depend on the schema file.
+-}
 recompile :: Config -> PluginRecompile
 recompile config =
     MaybeRecompile (fingerprintString (show (config.databaseUrl, config.schemaFile, config.nullability, config.types, config.baseline)))
 
-
 check :: TcGblEnv -> TcM ()
 check env = do
     config <- liftIO readConfig
@@ -87,7 +86,6 @@
                     Just (Left err) -> failAt s err
                     Just (Right statement) -> checkStatement config url s statement
 
-
 checkStatement :: Config -> String -> RealSrcSpan -> Statement -> TcM ()
 checkStatement config url s statement = do
     let file = unpackFS (srcSpanFile s)
@@ -102,7 +100,6 @@
             messages <- warnings config rules file statement.line fingerprint found
             forM_ messages (warning file (statement.line, statement.quote.column))
 
-
 -- | The statement as @hasql-check --inferred@ reads it.
 toInferred :: FilePath -> Statement -> Inferred
 toInferred file statement =
@@ -117,7 +114,6 @@
   where
     column c = Quote.Column (Text.pack c.haskellType) (Text.pack c.postgres) (map Text.pack c.labels) c.nullable
 
-
 failAt :: RealSrcSpan -> String -> TcM ()
 failAt s message =
     addErrAt (RealSrcSpan s mempty) (TcRnUnknownMessage (mkSimpleUnknownDiagnostic (mkPlainError noHints layout)))
@@ -127,13 +123,12 @@
         first : rest -> vcat [text first, nest 2 (vcat (map text rest))]
         [] -> empty
 
-
 -- * The statements in a type-checked module
 
-
--- | The quasi-quotes of a renamed module, with their spans and the line each
--- starts on.
-quasiQuotes :: Data a => a -> [(RealSrcSpan, Quote)]
+{- | The quasi-quotes of a renamed module, with their spans and the line each
+starts on.
+-}
+quasiQuotes :: (Data a) => a -> [(RealSrcSpan, Quote)]
 quasiQuotes x = here <> concat (gmapQ quasiQuotes x)
   where
     here = case cast x of
@@ -142,9 +137,9 @@
                 [(s, Quote (srcSpanStartCol s) (getOccString quoter) (unpackFS body))]
         _ -> []
 
-
--- | The innermost quote whose span holds the given span - that of the code it
--- was spliced into - with the line it starts on.
+{- | The innermost quote whose span holds the given span - that of the code it
+was spliced into - with the line it starts on.
+-}
 quoteAround :: [(RealSrcSpan, Quote)] -> RealSrcSpan -> Maybe (Int, Quote)
 quoteAround quotes s =
     case [(srcSpanStartLine o, q) | (o, q) <- around, all (\(o', _) -> o' `containsSpan` o) around] of
@@ -153,30 +148,28 @@
   where
     around = filter (\(o, _) -> o `containsSpan` s) quotes
 
-
--- | Each use of 'Hasql.Check.Inferred.statement' or
--- 'Hasql.Check.Inferred.resultless' - each quote - with its span and the
--- types of the dictionaries it is passed: GHC has worked out the types it is
--- used at.
-statements :: Data a => a -> [(RealSrcSpan, [Type])]
+{- | Each use of 'Hasql.Check.Inferred.statement' or
+'Hasql.Check.Inferred.resultless' - each quote - with its span and the
+types of the dictionaries it is passed: GHC has worked out the types it is
+used at.
+-}
+statements :: (Data a) => a -> [(RealSrcSpan, [Type])]
 statements x = here <> concat (gmapQ statements x)
   where
     here = case cast x of
         Just (L l (XExpr (WrapExpr w (HsVar _ (L _ v)))) :: LHsExpr GhcTc)
-            | getOccString v `elem` ["statement", "resultless"]
+            | getOccString v `elem` ["statement", "statementWith", "statementRow", "resultless", "resultlessWith", "rowsAffected", "rowsAffectedWith"]
             , Just m <- nameModule_maybe (varName v)
             , moduleNameString (moduleName m) == "Hasql.Check.Inferred"
             , RealSrcSpan s _ <- locA l ->
                 [(s, dictionaries w)]
         _ -> []
 
-
 dictionaries :: HsWrapper -> [Type]
 dictionaries = \case
     WpCompose a b -> dictionaries a <> dictionaries b
     WpEvApp (EvExpr e) -> [exprType e]
     _ -> []
-
 
 -- | A type, as "Hasql.Check.Ghc.Source" reads it.
 fromType :: Type -> Ty
