diff --git a/CHANGELOG.txt b/CHANGELOG.txt
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,9 @@
+3.0.5
+-----
+- Add `Row` type to represent dynamic query parameters
+  and results (e.g. to run "lightweight transactions"
+  which may yield different results)
+
 3.0.4
 -----
 - Update `iproute` upper bound
diff --git a/cql.cabal b/cql.cabal
--- a/cql.cabal
+++ b/cql.cabal
@@ -1,5 +1,5 @@
 name:                 cql
-version:              3.0.4
+version:              3.0.5
 synopsis:             Cassandra CQL binary protocol.
 stability:            experimental
 license:              MPL-2.0
@@ -64,6 +64,7 @@
         , time             >= 1.4    && < 2.0
         , transformers     >= 0.3    && < 0.5
         , uuid             >= 1.2.6  && < 2.0
+        , vector           >= 0.10   && < 1.0
 
 test-suite cql-tests
     type:             exitcode-stdio-1.0
diff --git a/src/Database/CQL/Protocol.hs b/src/Database/CQL/Protocol.hs
--- a/src/Database/CQL/Protocol.hs
+++ b/src/Database/CQL/Protocol.hs
@@ -138,7 +138,7 @@
     , Error     (..)
     , WriteType (..)
 
-      -- * Tuple and Record
+      -- * Row, Tuple and Record
     , module Database.CQL.Protocol.Tuple
     , module Database.CQL.Protocol.Record
     ) where
diff --git a/src/Database/CQL/Protocol/Response.hs b/src/Database/CQL/Protocol/Response.hs
--- a/src/Database/CQL/Protocol/Response.hs
+++ b/src/Database/CQL/Protocol/Response.hs
@@ -218,7 +218,7 @@
         m <- decodeMetaData
         n <- decodeInt
         let c = untag (count :: Tagged b Int)
-        unless (columnCount m == fromIntegral c) $
+        unless (c == -1 || columnCount m == fromIntegral c) $
             fail $ "column count: "
                 ++ show (columnCount m)
                 ++ " =/= "
@@ -229,7 +229,7 @@
         let message   = "expected: " ++ show expected ++ ", but got " ++ show ctypes
         unless (null expected) $
             fail $ "column-type error: " ++ message
-        RowsResult m <$> replicateM (fromIntegral n) (tuple v)
+        RowsResult m <$> replicateM (fromIntegral n) (tuple v ctypes)
     go 0x3 = SetKeyspaceResult <$> decodeKeyspace
     go 0x4 = PreparedResult <$> decodeQueryId <*> decodeMetaData <*> decodeMetaData
     go 0x5 = SchemaChangeResult <$> decodeSchemaChange v
diff --git a/src/Database/CQL/Protocol/Tuple.hs b/src/Database/CQL/Protocol/Tuple.hs
--- a/src/Database/CQL/Protocol/Tuple.hs
+++ b/src/Database/CQL/Protocol/Tuple.hs
@@ -15,6 +15,11 @@
     , check
     , tuple
     , store
+    , Row
+    , mkRow
+    , fromRow
+    , columnTypes
+    , rowLength
     ) where
 
 #if __GLASGOW_HASKELL__ < 710
diff --git a/src/Database/CQL/Protocol/Tuple/TH.hs b/src/Database/CQL/Protocol/Tuple/TH.hs
--- a/src/Database/CQL/Protocol/Tuple/TH.hs
+++ b/src/Database/CQL/Protocol/Tuple/TH.hs
@@ -11,6 +11,7 @@
 import Control.Monad
 import Data.Functor.Identity
 import Data.Serialize
+import Data.Vector (Vector, (!?))
 import Data.Word
 import Database.CQL.Protocol.Class
 import Database.CQL.Protocol.Codec (putValue, getValue)
@@ -18,12 +19,42 @@
 import Language.Haskell.TH
 import Prelude
 
+import qualified Data.Vector as Vec
+
+------------------------------------------------------------------------------
+-- Row
+
+-- | A row is a vector of 'Value's.
+data Row = Row
+    { types  :: !([ColumnType])
+    , values :: !(Vector Value)
+    } deriving (Eq, Show)
+
+-- | Convert a row element.
+fromRow :: Cql a => Int -> Row -> Either String a
+fromRow i r =
+    case values r !? i of
+        Nothing -> Left "out of bounds access"
+        Just  v -> fromCql v
+
+mkRow :: [(Value, ColumnType)] -> Row
+mkRow xs = let (v, t) = unzip xs in Row t (Vec.fromList v)
+
+rowLength :: Row -> Int
+rowLength r = Vec.length (values r)
+
+columnTypes :: Row -> [ColumnType]
+columnTypes = types
+
+------------------------------------------------------------------------------
+-- Tuples
+
 -- Database.CQL.Protocol.Tuple does not export 'PrivateTuple' but only
 -- 'Tuple' effectively turning 'Tuple' into a closed type-class.
 class PrivateTuple a where
     count :: Tagged a Int
     check :: Tagged a ([ColumnType] -> [ColumnType])
-    tuple :: Version -> Get a
+    tuple :: Version -> [ColumnType] -> Get a
     store :: Version -> Putter a
 
 class PrivateTuple a => Tuple a
@@ -32,23 +63,33 @@
 -- Manual instances
 
 instance PrivateTuple () where
-    count   = Tagged 0
-    check   = Tagged $ const []
-    tuple _ = return ()
-    store _ = const $ return ()
+    count     = Tagged 0
+    check     = Tagged $ const []
+    tuple _ _ = return ()
+    store _   = const $ return ()
 
 instance Tuple ()
 
 instance Cql a => PrivateTuple (Identity a) where
-    count   = Tagged 1
-    check   = Tagged $ typecheck [untag (ctype :: Tagged a ColumnType)]
-    tuple v = Identity <$> element v ctype
+    count     = Tagged 1
+    check     = Tagged $ typecheck [untag (ctype :: Tagged a ColumnType)]
+    tuple v _ = Identity <$> element v ctype
     store v (Identity a) = do
         put (1 :: Word16)
         putValue v (toCql a)
 
 instance Cql a => Tuple (Identity a)
 
+instance PrivateTuple Row where
+    count     = Tagged (-1)
+    check     = Tagged $ const []
+    tuple v t = Row t . Vec.fromList <$> mapM (getValue v) t
+    store v r = do
+        put (fromIntegral (rowLength r) :: Word16)
+        Vec.mapM_ (putValue v) (values r)
+
+instance Tuple Row
+
 ------------------------------------------------------------------------------
 -- Templated instances
 
@@ -101,7 +142,7 @@
 tupleDecl :: Int -> Q Clause
 tupleDecl n = do
     let v = mkName "v"
-    Clause [VarP v] (NormalB $ body v) <$> comb
+    Clause [VarP v, WildP] (NormalB $ body v) <$> comb
   where
     body v = UInfixE (var "combine") (var "<$>") (foldl1 star (elts v))
     elts v = replicate n (var "element" $$ VarE v $$ var "ctype")
