ログ日記

作業ログと日記とメモ

Haskellでデータベース操作

ぐぐったら2006年の自分の日記がヒットしてしまった…。あの頃から特に進展がないことに落ち込みつつ。
haskelldbは、haskelldb-hdbc、haskelldb-hsql、があって、それぞれHDBC、hsqlなどと関連している?


haskelldbのバージョンは0.13なのでちょっと躊躇する。


hsqlとHDBCでどんな感じのコードになるか。
hsql
HSQLを使ってMySQLに接続 - #3(2006-08-22)


HDBC
http://www.mokehehe.com/realworldhaskell/index.php?%A5%C7%A1%BC%A5%BF%A5%D9%A1%BC%A5%B9%C1%E0%BA%EE


うーん。とりあえずメジャーっぽいHDBCを入れてみる。

aptitude install libpq-dev
aptitude install libsqlite3-dev

cabal install HDBC-postgresql --global
cabal install HDBC-sqlite3 --global
import Database.HDBC
import Database.HDBC.Sqlite3

main = do
  conn <- connectSqlite3 "test.db"
  ret <- quickQuery' conn "SELECT * from test where id > 2" []
  mapM_ putStrLn $ map convRow ret
  disconnect conn

convRow :: [SqlValue] -> String
convRow [sqlId, sqlDesc] =
    show intid ++ ": " ++ desc
        where intid = (fromSql sqlId)::Integer
              desc = case fromSql sqlDesc of
                       Just x -> x
                       Nothing -> "NULL"
convRow x = fail $ "unexpected result: " ++ show x

で、サンプルをコピペ。
quickQuery'っていうぐらいだから、quickじゃない呼び出し方があるんだろうきっと。
プリペアドステートメントっぽい引数も受け取ってるのでこれで十分な気もする。




HDBC.hsをコピペメモ。

module Database.HDBC
    (-- * Introduction
     -- $introduction

     -- ** Features
     -- $features

     -- ** Available Drivers
     -- $drivers

     -- * Typing of transfer data
     SqlValue(..),
     toSql, fromSql, safeFromSql, nToSql, iToSql, posixToSql,

     -- * Database Connections
     IConnection,
     disconnect, clone,
     -- ** Wrapped Connections
     ConnWrapper(..), withWConn,
     -- ** Preparing Queries
     run, runRaw, sRun, prepare, quickQuery', quickQuery,

     -- ** Transaction Handling
     -- $transactions
     commit, rollback, withTransaction,

     -- ** Connection Inquiries
     hdbcDriverName, hdbcClientVer, proxiedClientName,
     proxiedClientVer, dbServerVer, dbTransactionSupport,
     getTables, describeTable,

     -- * Statements
     Statement,
     -- ** Execution
     execute, executeRaw, sExecute, executeMany, sExecuteMany,
     -- ** Fetching Results
     fetchRow, fetchRowAL, fetchRowMap, sFetchRow,
     fetchAllRows, fetchAllRows', fetchAllRowsAL, fetchAllRowsAL',
     fetchAllRowsMap, fetchAllRowsMap', sFetchAllRows, sFetchAllRows',
     getColumnNames,
     -- ** Statement Inquires
     describeResult,
     -- ** Miscellaneous
     finish, originalQuery,

     -- * Exceptions
     SqlError(..),
     throwSqlError,
     catchSql, handleSql, sqlExceptions, handleSqlError,

     -- * Column Types
     -- | These are defined in "Database.HDBC.ColTypes" but are
     -- available to programs importing "Database.HDBC" by default as well.
     -- See "Database.HDBC.ColTypes" for documentation.
     module Database.HDBC.ColTypes

     -- * Threading
     -- $threading

     -- * Copyright and License
     -- $legal
    )

クォーテーションがついているものは Strict version らしい。これはevaluateで取得した値をすべて評価している。遅延させずに実行したいときに使うのだろうか。


quickQueryはSqlValueの二重のリストを受け取るのだが、

ghci> conn <- connectSqlite3 "test1.db"
ghci> stmt <- prepare conn "SELECT * from test where id < 2"
ghci> execute stmt []
0
ghci> results <- fetchAllRowsAL stmt

こうするとステートメントを受け取って一行一行fetchするっぽい。
fetchRowやfetchRowMapかな?

*Main> :t fetchRow
fetchRow :: Statement -> IO (Maybe [SqlValue])

*Main> :t fetchRowMap
fetchRowMap
  :: Statement -> IO (Maybe (Data.Map.Map String SqlValue))

ちょっとめんどくさそう。


手続き型っぽく考えたらfetch→出力のループを回した方が色々節約できそうな気がするがHaskellだとさっぱり検討がつかない。
まぁ大量のデータを扱う段階になってから考えたらいいかな。