diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.6.7004.1
+
+* Fixed quadratic slowdown in `removeEmpty`.
+
+* Fixed `read` compatibility with time-1.9 in test suite.
+
 ## 0.6.7004.0
 
 * Many changes to the documentation that use the new names.  See entry
diff --git a/Doc/Tutorial/TutorialBasic.lhs b/Doc/Tutorial/TutorialBasic.lhs
--- a/Doc/Tutorial/TutorialBasic.lhs
+++ b/Doc/Tutorial/TutorialBasic.lhs
@@ -43,19 +43,19 @@
 features may be added later according to demand.
 
 A table is defined with the `table` function.  The syntax is
-simple.  You specify the types of the columns, the name of the table
-and the names of the columns in the underlying database, and whether
-the columns are required or optional.
+simple.  You specify the types of the fields, the name of the table
+and the names of the fields in the underlying database, and whether
+the fields are required or optional.
 
 (Note: This simple syntax is supported by an extra combinator that
-describes the shape of the container that you are storing the columns
+describes the shape of the container that you are storing the fields
 in.  In the first example we are using a tuple of size 3 and the
 combinator is called `p3`.  We'll see examples of others later.)
 
 The `Table` type constructor has two arguments.  The first one tells
-us what columns we can write to the table and the second what columns
+us what fields we can write to the table and the second what fields
 we can read from the table.  In this document we will always make all
-columns required, so the write and read types will be the same.  All
+fields required, so the write and read types will be the same.  All
 `Table` types will have the same type argument repeated twice.  In the
 manipulation tutorial you can see an example of when they might differ.
 
@@ -167,8 +167,8 @@
 Projection
 ==========
 
-"Projection" means discarding some of the columns of our query, for
-example we might want to discard the "address" column of our
+"Projection" means discarding some of the fields of our query, for
+example we might want to discard the "address" field of our
 `personSelect`.
 
 Projection gives us our first example of using "arrow notation" to
@@ -179,7 +179,7 @@
 
 Here we run the `personSelect` passing in () to signify "zero
 arguments".  We pattern match on the results and return only the
-columns we are interested in.
+fields we are interested in.
 
 > nameAge :: Select (Field SqlText, Field SqlInt4)
 > nameAge = proc () -> do
@@ -374,7 +374,7 @@
 NULLs in SQL have been the source of a lot of complaints, but as
 Haskell programmers we know that there is nothing wrong with
 nullability as long is it is reflected in the type system.  Nullable
-columns are indicated with the `Nullable` type constructor.
+fields are indicated with the `FieldNullable` type constructor.
 
 For example, suppose we have an employee table which records the name
 of each employee and the name of their boss.  If their boss is
@@ -470,9 +470,9 @@
 My Town".
 
 The types are of the form `SelectArr a ()`.  This means that they read
-columns of type `a` but do not return any columns.  (Note: `Select` is
+fields of type `a` but do not return any fields.  (Note: `Select` is
 just a synonym for `SelectArr ()` which means that it is a `SelectArr`
-that does not read any columns.)
+that does not read any fields.)
 
 > restrictIsTwenties :: SelectArr (Field SqlInt4) ()
 > restrictIsTwenties = proc age -> do
@@ -642,7 +642,7 @@
 
 Note: In `widgetTable` and `aggregateWidgets` we see more explicit
 uses of our Template Haskell derived code.  We use the 'pWidget'
-"adaptor" to specify how columns are aggregated.  Note that this is
+"adaptor" to specify how fields are aggregated.  Note that this is
 yet another example of avoiding a headache by keeping your datatype
 fully polymorphic, because the 'count' aggregator changes a 'Field
 String' into a 'Field Int64'.
@@ -654,8 +654,8 @@
 left to be added as a simple starter project for a new Opaleye
 contributer!)
 
-Because left joins can change non-nullable columns into nullable
-columns we have to make sure the type of the output supports
+Because left joins can change non-nullable fields into nullable
+fields we have to make sure the type of the output supports
 nullability.  We introduce the following type synonym for this
 purpose, which is just a notational convenience.
 
@@ -807,9 +807,9 @@
 the following type
 
 > -- runSelect :: Database.PostgreSQL.Simple.Connection
-> --          -> Select columns -> IO [haskells]
+> --          -> Select fields -> IO [haskells]
 
-It converts a "record" of Opaleye columns to a list of "records" of
+It converts a "record" of Opaleye fields to a list of "records" of
 Haskell values.  Like `leftJoin` this particular formulation uses
 typeclasses so please put type signatures on everything in sight to
 minimize the number of confusing error messages!
@@ -822,9 +822,9 @@
 >                  -> IO [(String, Int, String)]
 > runTwentiesSelect = runSelect
 
-Note that nullable columns are indicated with the Nullable type
+Note that nullable fields are indicated with the FieldNullable type
 constructor, and these are converted to Maybe when executed.  If we
-have a table with a nullable column then Nullable columns turn into
+have a table with a nullable field then FieldNullables turn into
 Maybes.  We could run the query `selectTable employeeTable` like this.
 
 > runEmployeesSelect :: PGS.Connection
diff --git a/Doc/Tutorial/TutorialBasicMonomorphic.lhs b/Doc/Tutorial/TutorialBasicMonomorphic.lhs
--- a/Doc/Tutorial/TutorialBasicMonomorphic.lhs
+++ b/Doc/Tutorial/TutorialBasicMonomorphic.lhs
@@ -45,19 +45,19 @@
 features may be added later according to demand.
 
 A table is defined with the `table` function.  The syntax is
-simple.  You specify the types of the columns, the name of the table
-and the names of the columns in the underlying database, and whether
-the columns are required or optional.
+simple.  You specify the types of the fields, the name of the table
+and the names of the fields in the underlying database, and whether
+the fields are required or optional.
 
 (Note: This simple syntax is supported by an extra combinator that
-describes the shape of the container that you are storing the columns
+describes the shape of the container that you are storing the fields
 in.  In the first example we are using a tuple of size 3 and the
 combinator is called `p3`.  We'll see examples of others later.)
 
 The `Table` type constructor has two arguments.  The first one tells
-us what columns we can write to the table and the second what columns
+us what fields we can write to the table and the second what fields
 we can read from the table.  In this document we will always make all
-columns required, so the write and read types will be the same.  All
+fields required, so the write and read types will be the same.  All
 `Table` types will have the same type argument repeated twice.  In the
 manipulation tutorial you can see an example of when they might differ.
 
@@ -268,7 +268,7 @@
 
 Note: In `widgetTable` and `aggregateWidgets` we see more explicit
 uses of our Template Haskell derived code.  We use the 'pWidget'
-"adaptor" to specify how columns are aggregated.  Note that this is
+"adaptor" to specify how fields are aggregated.  Note that this is
 yet another example of avoiding a headache by keeping your datatype
 fully polymorphic, because the 'count' aggregator changes a 'Field
 String' into a 'Field Int64'.
@@ -280,8 +280,8 @@
 left to be added as a simple starter project for a new Opaleye
 contributer!)
 
-Because left joins can change non-nullable columns into nullable
-columns we have to make sure the type of the output supports
+Because left joins can change non-nullable fields into nullable
+fields we have to make sure the type of the output supports
 nullability.  We introduce the following type synonym for this
 purpose, which is just a notational convenience.
 
@@ -377,7 +377,7 @@
 > -- runSelect :: Database.PostgreSQL.Simple.Connection
 > --          -> Select fields -> IO [haskells]
 
-It converts a "record" of Opaleye columns to a list of "records" of
+It converts a "record" of Opaleye fields to a list of "records" of
 Haskell values.  Like `leftJoin` this particular formulation uses
 typeclasses so please put type signatures on everything in sight to
 minimize the number of confusing error messages!
diff --git a/Doc/Tutorial/TutorialBasicTypeFamilies.lhs b/Doc/Tutorial/TutorialBasicTypeFamilies.lhs
--- a/Doc/Tutorial/TutorialBasicTypeFamilies.lhs
+++ b/Doc/Tutorial/TutorialBasicTypeFamilies.lhs
@@ -54,17 +54,17 @@
 features may be added later according to demand.
 
 A table is defined with the `table` function.  The syntax is
-simple.  You specify the types of the columns, the name of the table
-and the names of the columns in the underlying database.
+simple.  You specify the types of the fields, the name of the table
+and the names of the fields in the underlying database.
 
 (Note: This simple syntax is supported by an extra combinator that
-describes the shape of the container that you are storing the columns
+describes the shape of the container that you are storing the fields
 in.  In the first example we are using a tuple of size 3 and the
 combinator is called `p3`.  We'll see examples of others later.)
 
 The `Table` type constructor has two arguments.  The first one tells
-us what columns we can write to the table and the second what columns
-we can read from the table.  In this case all columns are required, so
+us what fields we can write to the table and the second what fields
+we can read from the table.  In this case all fields are required, so
 the write and read types will be the same.
 
 > personTable :: Table (Field SqlText, Field SqlInt4, Field SqlText)
@@ -288,7 +288,7 @@
 
 Note: In `widgetTable` and `aggregateWidgets` we see more explicit
 uses of our Template Haskell derived code.  We use the 'pWidget'
-"adaptor" to specify how columns are aggregated.
+"adaptor" to specify how fields are aggregated.
 
 Outer join
 ==========
@@ -367,9 +367,9 @@
 the following type
 
 > -- runSelect :: Database.PostgreSQL.Simple.Connection
-> --          -> Select columns -> IO [haskells]
+> --          -> Select fields -> IO [haskells]
 
-It converts a "record" of Opaleye columns to a list of "records" of
+It converts a "record" of Opaleye fields to a list of "records" of
 Haskell values.  Like `leftJoin` this particular formulation uses
 typeclasses so please put type signatures on everything in sight to
 minimize the number of confusing error messages!
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2019 Purely Agile Limited
+Copyright (c) 2014-2019 Purely Agile Limited, Tom Ellis
 
 All rights reserved.
 
diff --git a/Test/Test.hs b/Test/Test.hs
--- a/Test/Test.hs
+++ b/Test/Test.hs
@@ -1090,12 +1090,12 @@
   it "sqlBool" $ testLiteral O.sqlBool True
   it "sqlUUID" $ testLiteral O.sqlUUID (read "c2cc10e1-57d6-4b6f-9899-38d972112d8c")
   it "sqlDay" $ testLiteral O.sqlDay (read "2018-11-29")
-  it "sqlUTCTime" $ testLiteral O.sqlUTCTime (read "2018-11-29 11:22:33")
+  it "sqlUTCTime" $ testLiteral O.sqlUTCTime (read "2018-11-29 11:22:33 UTC")
   it "sqlLocalTime" $ testLiteral O.sqlLocalTime (read "2018-11-29 11:22:33")
 
   -- ZonedTime has no Eq instance, so we compare on the result of 'zonedTimeToUTC'
   it "sqlZonedTime" $
-    let value = read "2018-11-29 11:22:33" :: Time.ZonedTime in
+    let value = read "2018-11-29 11:22:33 UTC" :: Time.ZonedTime in
     testH (pure (O.sqlZonedTime value))
           (\r -> map Time.zonedTimeToUTC r `shouldBe` [Time.zonedTimeToUTC value])
 
diff --git a/opaleye.cabal b/opaleye.cabal
--- a/opaleye.cabal
+++ b/opaleye.cabal
@@ -1,6 +1,6 @@
 name:            opaleye
 copyright:       Copyright (c) 2014-2019 Purely Agile Limited
-version:         0.6.7004.0
+version:         0.6.7004.1
 synopsis:        An SQL-generating DSL targeting PostgreSQL
 description:     An SQL-generating DSL targeting PostgreSQL.  Allows
                  Postgres queries to be written within Haskell in a
@@ -18,7 +18,7 @@
                  CHANGELOG.md
                  *.md
                  Doc/*.md
-tested-with:     GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2
+tested-with:     GHC==8.8.1, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2
 
 source-repository head
   type:     git
@@ -37,7 +37,7 @@
     , postgresql-simple   >= 0.5.3   && < 0.7
     , pretty              >= 1.1.1.0 && < 1.2
     , product-profunctors >= 0.6.2   && < 0.11
-    , profunctors         >= 4.0     && < 5.5
+    , profunctors         >= 4.0     && < 5.6
     , scientific          >= 0.3     && < 0.4
     , semigroups          >= 0.13    && < 0.20
     , text                >= 0.11    && < 1.3
diff --git a/src/Opaleye/Field.hs b/src/Opaleye/Field.hs
--- a/src/Opaleye/Field.hs
+++ b/src/Opaleye/Field.hs
@@ -22,7 +22,7 @@
 
 type instance Field_ 'NonNullable a = C.Column a
 type instance Field_ 'Nullable a = C.Column (C.Nullable a)
-  
+
 type FieldNullable  a = Field_ 'Nullable a
 type Field a = Field_ 'NonNullable a
 
diff --git a/src/Opaleye/Internal/Optimize.hs b/src/Opaleye/Internal/Optimize.hs
--- a/src/Opaleye/Internal/Optimize.hs
+++ b/src/Opaleye/Internal/Optimize.hs
@@ -37,8 +37,7 @@
     PQ.unit      = return PQ.Unit
   , PQ.empty     = const Nothing
   , PQ.baseTable = return .: PQ.BaseTable
-  , PQ.product   = \x y -> PQ.Product <$> (T.traverse removeEmpty
-                                               =<< T.sequence x)
+  , PQ.product   = \x y -> PQ.Product <$> T.sequence x
                                       <*> pure y
   , PQ.aggregate = fmap . PQ.Aggregate
   , PQ.distinctOnOrderBy = \mDistinctOns -> fmap . PQ.DistinctOnOrderBy mDistinctOns
diff --git a/src/Opaleye/Internal/Print.hs b/src/Opaleye/Internal/Print.hs
--- a/src/Opaleye/Internal/Print.hs
+++ b/src/Opaleye/Internal/Print.hs
@@ -170,4 +170,3 @@
   HPrint.ppDelete delete
   $$ text "RETURNING"
   <+> HPrint.commaV HPrint.ppSqlExpr (NEL.toList returnExprs)
-
diff --git a/src/Opaleye/Internal/Table.hs b/src/Opaleye/Internal/Table.hs
--- a/src/Opaleye/Internal/Table.hs
+++ b/src/Opaleye/Internal/Table.hs
@@ -127,6 +127,11 @@
   (optionalW columnName)
   (View (Column (HPQ.BaseTableAttrExpr columnName)))
 
+-- | 'readOnly' is for columns that you must omit on writes, such as
+--  SERIAL columns intended to auto-increment only.
+readOnly :: String -> TableColumns () (Column a)
+readOnly = lmap (const Nothing) . optional
+
 class TableColumn writeType sqlType | writeType -> sqlType where
     -- | Do not use.  Use 'tableField' instead.  Will be deprecated in
     -- 0.7.
diff --git a/src/Opaleye/Manipulation.hs b/src/Opaleye/Manipulation.hs
--- a/src/Opaleye/Manipulation.hs
+++ b/src/Opaleye/Manipulation.hs
@@ -498,4 +498,3 @@
           -> IO Int64
           -- ^ The number of rows deleted
 runDelete conn = PGS.execute_ conn . fromString .: arrangeDeleteSql
-
diff --git a/src/Opaleye/Table.hs b/src/Opaleye/Table.hs
--- a/src/Opaleye/Table.hs
+++ b/src/Opaleye/Table.hs
@@ -67,6 +67,7 @@
                       T.Table,
                       T.tableField,
                       T.optional,
+                      T.readOnly,
                       T.required,
                       -- * Querying tables
                       selectTable,
