valderman / selda

A type-safe, high-level SQL library for Haskell
https://selda.link
MIT License
478 stars 58 forks source link

get the underlying indicies for a Selector #148

Open cdepillabout opened 3 years ago

cdepillabout commented 3 years ago

I'd like to be able to get the underlying indices contained in a Selector.

It seems like the easiest thing to do here would be to expose the selectorIndex getter, possibly in Database.Selda.Unsafe.


My use-case for this is that I'd like to be able to programmatically generate the column name for a given selector and table.

I'd like to write a function like the following:

colNameFor :: Table t -> Selector t a -> SeldaUnsafe.QueryFragment
colNameFor tbl s =
  let colInfos = tableColumnInfos $ tableInfo tbl
      idx = selectorIndex s
      colInfo = colInfos !! idx
  in fromColName $ colName colInfo

I'd like to be able to have some minimal amount of schema checking when writing raw SQL.

For instance, I want to use a SELECT DISTINCT ON statement (which it appears Selda doesn't have support for).

I want to write a function like the following:

data Foo = Foo
  { name :: Text
  , a :: Text
  , b :: Text
  }

listFoos :: Query s (Row s Foo)
listFoos = do
  let colName' = colNameFor foosTable #name
      colA = colNameFor foosTable #a
      colB = colNameFor foosTable #b
      q =
        "SELECT DISTINCT ON ( " <> colName' <> ", " <> colA <> ", " <> colB <> ") * " <>
        "  FROM foos " <>
        "ORDER BY " <> colName' <> ", " <> colA <> ", " <> colB
      allColNames =
        fmap colName $ tableColumnInfos $ tableInfo foosTable
  rawQuery allColNames q

Using the colNameFor function is slightly safer than just writing something like the following:

listFoos' :: Query s (Row s Foo)
listFoos' = do
  let q =
        "SELECT DISTINCT ON ( name, a, b ) * " <>
        "  FROM foos " <>
        "ORDER BY name, a, b"
      allColNames =
        fmap colName $ tableColumnInfos $ tableInfo foosTable
  rawQuery allColNames q

colNameFor makes this listFoos slightly more robust in the case where columns have been renamed. When columns are renamed in the database, listFoos should fail to compile (because there will no longer be a matching IsLabel instance for the Table type), while listFoos' will continue to compile.