Skip to content

Instantly share code, notes, and snippets.

@singpolyma
Created August 12, 2016 21:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save singpolyma/f5de70c79a9636981a4ffb7ccd66eff0 to your computer and use it in GitHub Desktop.
Save singpolyma/f5de70c79a9636981a4ffb7ccd66eff0 to your computer and use it in GitHub Desktop.
*-simple helpers
module SqlSimpleHelpers where
import Prelude hiding (all)
import Control.Applicative ((<*>))
import Data.Tagged (Tagged(..), asTaggedTypeOf)
import Data.Text (Text)
import qualified Data.Text as T
class SqlColumns a where
columns :: Tagged a [Text] -- ^ Columns representend in the Haskell data, in order used in ToRow/FromRow
class SqlTable a where
table :: Tagged a Text -- ^ Name of table data will normally come from for this type
class SqlPrimaryKey a where
primaryKey :: Tagged a Text -- ^ The column that is the primary key for this data
instance (SqlColumns a) => SqlColumns [a] where
columns = result
where
[w] = undefined `asTaggedTypeOf` result
result = Tagged (w `witness` columns)
instance (SqlTable a) => SqlTable [a] where
table = result
where
[w] = undefined `asTaggedTypeOf` result
result = Tagged (w `witness` table)
instance (SqlPrimaryKey a) => SqlPrimaryKey [a] where
primaryKey = result
where
[w] = undefined `asTaggedTypeOf` result
result = Tagged (w `witness` primaryKey)
-- | Use this when your data type does not contain the primary key, but you want to access it
data WithPrimaryKey key model = WithPrimaryKey key model
-- TODO: there are obvious ToRow/FromRow instances for WithPrimaryKey, but then we need to depend on the *-simple packages
instance (SqlColumns model, SqlPrimaryKey model) => SqlColumns (WithPrimaryKey key model) where
columns = result
where
WithPrimaryKey _ w = undefined `asTaggedTypeOf` result
result = Tagged ((w `witness` primaryKey) : (w `witness` columns))
instance (SqlTable model) => SqlTable (WithPrimaryKey key model) where
table = result
where
WithPrimaryKey _ w = undefined `asTaggedTypeOf` result
result = Tagged (w `witness` table)
instance (SqlPrimaryKey model) => SqlPrimaryKey (WithPrimaryKey key model) where
primaryKey = result
where
WithPrimaryKey _ w = undefined `asTaggedTypeOf` result
result = Tagged (w `witness` primaryKey)
-- TODO: these "with" are great for insert/select one, but not great for more complex queries
with :: (Text -> a -> b) -> Tagged a Text -> a -> b
with f (Tagged q) v = f q v
with_ :: (Text -> m a) -> Tagged a Text -> m a
with_ f (Tagged q) = f q
withKey :: (Text -> key -> m a) -> Tagged a Text -> key -> m a
withKey f (Tagged q) k = f q k
insert :: (SqlTable a, SqlColumns a) => Tagged a Text
insert = Tagged (\t c -> T.concat [
T.pack "INSERT INTO ",
t,
T.pack " (",
T.intercalate (T.pack ",") c,
T.pack ") VALUES (",
T.intercalate (T.pack ",") (map (const $ T.pack "?") c),
T.pack ")"
]) <*> table <*> columns
all :: (SqlTable a, SqlColumns a) => Tagged a Text
all = Tagged (\t c -> T.concat [
T.pack "SELECT ",
T.intercalate (T.pack ",") c,
T.pack " FROM ",
t
]) <*> table <*> columns
one :: (SqlTable a, SqlColumns a, SqlPrimaryKey a) => Tagged a Text
one = Tagged (\q p -> T.concat [
q,
T.pack " WHERE ",
p,
T.pack " = ?"
]) <*> all <*> primaryKey
update :: (SqlTable a, SqlColumns a) => Tagged a Text
update = Tagged (\t c -> T.concat [
T.pack "UPDATE ",
t,
T.pack " SET ",
T.intercalate (T.pack ",")
(map (\name -> T.concat [name, T.pack " = c.", name]) c),
T.pack " FROM (VALUES (",
T.intercalate (T.pack ",") (map (const $ T.pack "?") c),
T.pack ")) AS c(",
T.intercalate (T.pack ",") c,
T.pack ")"
]) <*> table <*> columns
-- | Just for playing around in GHCI
printExecute :: (Show v) => Text -> v -> IO ()
printExecute q v = print (q, v)
-- | Just for playing around in GHCI
-- Produces undefined because we're not actually doing the query
printQuery :: (Show v) => Text -> v -> IO a
printQuery q v = print (q, v) >> return undefined
-- | Just for playing around in GHCI
-- Produces undefined because we're not actually doing the query
printQuery_ :: Text -> IO a
printQuery_ q = print q >> return undefined
witness :: w -> Tagged w a -> a
witness _ (Tagged x) = x
{-
EXAMPLE
data User = User {
name :: Text,
age :: Int
} deriving (Show)
instance FromRow User where
fromRow = User <$> field <*> field
instance ToRow User where
toRow (User name age)= [toField name, toField age]
instance SqlColumns User where
columns = Tagged [T.pack "name", T.pack "age"]
instance SqlTable User where
table = Tagged (T.pack "users")
instance SqlPrimaryKey User where
primaryKey = Tagged (T.pack "user_id")
BEST USE CASES FOR WITH
with printExecute insert (User (T.pack "Dave") 42)
with printExecute insert [User (T.pack "Bob") 54, User (T.pack "Steve") 23]
withKey printQuery one (Only 1) :: IO User
THESE WORK BUT NEED MORE THOUGHT
with_ printQuery_ all :: IO [User]
with printExecute update [User (T.pack "Dave") 42, User (T.pack "Bob") 15]
-}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment