diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,21 @@
 # Changelog
 
+## 0.5.2.0
+
+A statement with `$1.$field` param fields could not be checked from a `.hie`
+file, nor by the plugin: `hasql-check-hie` said its types were not known
+where it is written. A field's type is only known once GHC has solved its
+`HasField`, after it has already settled each slot's Postgres type as an
+unsolved `PgColumnType a`; so GHC recorded `PgColumnType Text` rather than
+`"text"`, which `hasql-check-hie` cannot reduce. A slot's Postgres type and
+labels now come from `SlotType`, whose instances GHC cannot choose between
+until the field's type is known, so they are recorded reduced.
+
+`--inferred` took the number of parameters a statement with param fields is
+sent to be its number of placeholders, so it reported each placeholder past
+the first parameter's as a parameter the statement does not use. It now
+counts the statement's parameters, as the quoter's pattern takes them.
+
 ## 0.5.1.0
 
 The quoters of `Hasql.Check.Inferred.TH` now take the Haskell targets and
diff --git a/hasql-check.cabal b/hasql-check.cabal
--- a/hasql-check.cabal
+++ b/hasql-check.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.0
 name:            hasql-check
-version:         0.5.1.0
+version:         0.5.2.0
 synopsis:        Check hasql-th statements against a live PostgreSQL schema
 description:
   Finds the hasql-th quasiquotes in Haskell source files and PREPAREs each
diff --git a/src/Hasql/Check/Inferred.hs b/src/Hasql/Check/Inferred.hs
--- a/src/Hasql/Check/Inferred.hs
+++ b/src/Hasql/Check/Inferred.hs
@@ -1033,8 +1033,25 @@
 slotsNone = Slots mempty
 
 -- | One parameter slot, sending what the projection picks out.
-slotsOne :: forall a p. (Column a) => (p -> a) -> Slots '[PgColumnType a] '[PgColumnLabels a] '[a] p
+slotsOne :: forall a p s l. (Column a, SlotType a s l) => (p -> a) -> Slots '[s] '[l] '[a] p
 slotsOne f = Slots (f >$< E.param encodeColumn)
+
+{- | A slot's Postgres type and labels, 'PgColumnType' and 'PgColumnLabels'
+of the type it sends, worked out only once that type is known.
+
+A param field's type is only known once GHC has solved its 'HasField',
+after it has settled the slot's type. Had the slot carried
+@'PgColumnType' a@ itself, GHC would have recorded the family unreduced,
+as @'PgColumnType' Text@ rather than @"text"@, and @hasql-check-hie@,
+which cannot reduce it, could not check the statement. Like 'Column''s,
+these instances cannot be told apart until the type is known, so GHC waits
+for it before reducing the families.
+-}
+class SlotType a (s :: Symbol) (l :: [Symbol]) | a -> s l
+
+instance {-# OVERLAPPABLE #-} (s ~ PgColumnType a, l ~ PgColumnLabels a) => SlotType a s l
+
+instance (s ~ PgColumnType (Maybe a), l ~ PgColumnLabels (Maybe a)) => SlotType (Maybe a) s l
 
 -- | One more slot, appending its type.
 slotsAp :: Slots ps ls hs p -> Slots qs ms is p -> Slots (Append ps qs) (Append ls ms) (Append hs is) p
diff --git a/src/Hasql/Check/Inferred/Quote.hs b/src/Hasql/Check/Inferred/Quote.hs
--- a/src/Hasql/Check/Inferred/Quote.hs
+++ b/src/Hasql/Check/Inferred/Quote.hs
@@ -38,7 +38,7 @@
 import Hasql.Check.Annotate (annotate, typename, untypedLiterals)
 import Hasql.Check.Finding (Finding (..), statementHash)
 import Hasql.Check.Inferred (Template (..), render)
-import Hasql.Check.Inferred.TH (Parsed (..), parsed)
+import Hasql.Check.Inferred.TH (Parsed (..), Slot (..), parsed)
 import Hasql.Check.Nullability (Catalogue, query)
 import Hasql.Check.Nullability qualified as Nullability
 import Hasql.Check.Types qualified as Types
@@ -190,9 +190,14 @@
                         -- types hasql sends, it takes them all, used or not.
                         -- In the numbering the statement was written in,
                         -- which lowering renumbers: each slot knows the
-                        -- parameter it came from.
+                        -- parameter it came from. A param field sends a
+                        -- field of its parameter, so where there are any the
+                        -- Haskell type sends as many parameters as the quoter
+                        -- made its pattern take, not one per slot.
                         let used = p.parsedUsed
-                            sends = length q.params
+                            sends
+                                | or [True | Slot _ (Just _) <- p.parsedSlots] = p.parsedArity
+                                | otherwise = length q.params
                             paramErrors =
                                 [ concat ["the Haskell type sends parameter $", show n, ", which the statement does not use"]
                                 | n <- [1 .. sends]
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -225,6 +225,12 @@
     itWithOuter "annotates the parameters in the order they are sent, not written" \conn ->
         runInferred conn [] [inferredQuote "resultlessStatement" "insert into tag (label, id) values ($2, $1)" [text, int32] Nothing]
             `shouldReturn` (ExitSuccess, "")
+
+    -- A type per slot, so three for two parameters: the record's two fields,
+    -- and $2. None of them is a parameter the statement does not use.
+    itWithOuter "counts a record parameter once, however many fields it sends" \conn ->
+        runInferred conn [] [inferredQuote "resultlessStatement" "update tag set id = $1.$id where id = $1.$other and label = $2" [int32, int32, text] Nothing]
+            `shouldReturn` (ExitSuccess, "")
 #else
 syntaxSpec =
     itWithOuter "rejects Haskell targets, which upstream hasql-th does not accept" \conn -> do
