Skip to content

Commit

Permalink
Use explicit list of columns in SELECT queries instead of using `SE…
Browse files Browse the repository at this point in the history
…LECT *`

This avoids issues when the database schema is being migrated
  • Loading branch information
mpscholten committed Sep 27, 2021
1 parent 5fff5f6 commit 0a2cbdb
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 62 deletions.
2 changes: 1 addition & 1 deletion IHP/AuthSupport/Controller/Sessions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,6 @@ class ( Typeable record
--
-- > usersQueryBuilder = query @User |> filterWhere (#isGuest, False)
--
usersQueryBuilder :: (GetModelByTableName (GetTableName record) ~ record) => QueryBuilder (GetTableName record)
usersQueryBuilder :: (GetModelByTableName (GetTableName record) ~ record, Table record) => QueryBuilder (GetTableName record)
usersQueryBuilder = query @record
{-# INLINE usersQueryBuilder #-}
10 changes: 5 additions & 5 deletions IHP/ModelSupport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,8 @@ class
--

tableName :: Text
default tableName :: forall model. (KnownSymbol (GetTableName model)) => Text
tableName = symbolToText @(GetTableName model)
default tableName :: forall model. (KnownSymbol (GetTableName record)) => Text
tableName = symbolToText @(GetTableName record)
{-# INLINE tableName #-}

-- | Returns the table name of a given model as a bytestring.
Expand All @@ -459,8 +459,8 @@ class
-- "users"
--
tableNameByteString :: ByteString
default tableNameByteString :: forall model. (KnownSymbol (GetTableName model)) => ByteString
tableNameByteString = symbolToByteString @(GetTableName model)
default tableNameByteString :: forall model. (KnownSymbol (GetTableName record)) => ByteString
tableNameByteString = symbolToByteString @(GetTableName record)
{-# INLINE tableNameByteString #-}

-- | Returns the list of column names for a given model
Expand All @@ -470,7 +470,7 @@ class
-- >>> columnNames @User
-- ["id", "email", "created_at"]
--
columnNames :: [Text]
columnNames :: [ByteString]

-- | Returns WHERE conditions to match an entity by it's primary key
--
Expand Down
25 changes: 16 additions & 9 deletions IHP/QueryBuilder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ instance {-# OVERLAPPABLE #-} DefaultScope table where
{-# INLINE defaultScope #-}
defaultScope queryBuilder = queryBuilder

instance Default (QueryBuilder table) where
instance Table (GetModelByTableName table) => Default (QueryBuilder table) where
{-# INLINE def #-}
def = NewQueryBuilder
def = NewQueryBuilder { columns = columnNames @(GetModelByTableName table) }

data MatchSensitivity = CaseSensitive | CaseInsensitive deriving (Show, Eq)

Expand Down Expand Up @@ -193,7 +193,7 @@ instance (KnownSymbol foreignTable, foreignModel ~ GetModelByTableName foreignTa


data QueryBuilder (table :: Symbol) =
NewQueryBuilder
NewQueryBuilder { columns :: ![ByteString] }
| DistinctQueryBuilder { queryBuilder :: !(QueryBuilder table) }
| DistinctOnQueryBuilder { queryBuilder :: !(QueryBuilder table), distinctOnColumn :: !ByteString }
| FilterByQueryBuilder { queryBuilder :: !(QueryBuilder table), queryFilter :: !(ByteString, FilterOperator, Action), applyLeft :: !(Maybe ByteString), applyRight :: !(Maybe ByteString) }
Expand Down Expand Up @@ -224,6 +224,7 @@ data SQLQuery = SQLQuery
, orderByClause :: ![OrderByClause]
, limitClause :: !(Maybe ByteString)
, offsetClause :: !(Maybe ByteString)
, columns :: ![ByteString]
} deriving (Show, Eq)

-- | Needed for the 'Eq QueryBuilder' instance
Expand Down Expand Up @@ -276,15 +277,15 @@ instance SetField "offsetClause" SQLQuery (Maybe ByteString) where setField valu
-- > query @User
-- > |> filterWhere (#active, True)
-- > |> fetch
query :: forall model table. (table ~ GetTableName model) => DefaultScope table => QueryBuilder table
query = (defaultScope @table) NewQueryBuilder
query :: forall model table. (table ~ GetTableName model, Table model) => DefaultScope table => QueryBuilder table
query = (defaultScope @table) NewQueryBuilder { columns = columnNames @model }
{-# INLINE query #-}

{-# INLINE buildQuery #-}
buildQuery :: forall table queryBuilderProvider joinRegister. (KnownSymbol table, HasQueryBuilder queryBuilderProvider joinRegister) => queryBuilderProvider table -> SQLQuery
buildQuery queryBuilderProvider = buildQueryHelper $ getQueryBuilder queryBuilderProvider
where
buildQueryHelper NewQueryBuilder =
buildQueryHelper NewQueryBuilder { columns } =
let tableName = symbolToByteString @table
in SQLQuery
{ queryIndex = trace (Prelude.show $ getQueryIndex queryBuilderProvider) getQueryIndex queryBuilderProvider
Expand All @@ -296,6 +297,7 @@ buildQuery queryBuilderProvider = buildQueryHelper $ getQueryBuilder queryBuilde
, orderByClause = []
, limitClause = Nothing
, offsetClause = Nothing
, columns
}
buildQueryHelper DistinctQueryBuilder { queryBuilder } = queryBuilder
|> buildQueryHelper
Expand Down Expand Up @@ -368,7 +370,7 @@ toSQL queryBuilderProvider = toSQL' (buildQuery queryBuilderProvider)
{-# INLINE toSQL #-}

toSQL' :: SQLQuery -> (ByteString, [Action])
toSQL' sqlQuery@SQLQuery { queryIndex, selectFrom, distinctClause, distinctOnClause, orderByClause, limitClause, offsetClause } =
toSQL' sqlQuery@SQLQuery { queryIndex, selectFrom, distinctClause, distinctOnClause, orderByClause, limitClause, offsetClause, columns } =
(DeepSeq.force theQuery, theParams)
where
!theQuery =
Expand All @@ -388,8 +390,13 @@ toSQL' sqlQuery@SQLQuery { queryIndex, selectFrom, distinctClause, distinctOnCla
]

selectors :: ByteString
selectors = ByteString.intercalate ", " $ catMaybes [queryIndex , Just (selectFrom <> ".*")]

selectors = ByteString.intercalate ", " $ (catMaybes [queryIndex]) <> selectFromWithColumns
where
-- Generates a string like: `posts.id, posts.title, posts.body`
selectFromWithColumns :: [ByteString]
selectFromWithColumns =
columns
|> map (\column -> selectFrom <> "." <> column)
fromClause :: ByteString
fromClause = selectFrom

Expand Down
Loading

0 comments on commit 0a2cbdb

Please sign in to comment.