Haskell で SQL Server へ ODBC接続

【データ ソース (ODBC)作成】

  • 「コントロール パネル\システムとメンテナンス\管理ツール」から「データ ソース (ODBC)」を開きます。
  • システムDSN のタブを開き「追加」で「SQL Server」を選択。
  • 「名前」(サンプルでは「DSN=DSNname;」)を入力し、「サーバー」を選択。「次へ(N)>」
  • 「ユーザが入力するSQLServer用のログインIDとパスワードを使う」を選択。入力。「次へ(N)>」(サンプルでは「UID=user;PWD=password;」)
  • 「既定のデータベースを変更する」にチェックし、データベースを選択。「次へ(N)>」
  • 「出力時の通貨、数値、日付、時刻の形式にはシステムの地域設定を利用する」にチェックしたけど・・・どうかな。で、「完了」
import Database.HDBC
import Database.HDBC.ODBC
import qualified Data.ByteString as B
import Data.Char
import Numeric
import Cinnamon.Ucs          (ucs4ToSjis, sjisToUcs4)

main = do
    odbc <- connectODBC "DSN=DSNname;UID=user;PWD=password;"
    xs <- getTables odbc
    print xs

    stmt <- prepare odbc "SELECT * FROM table"
    execute stmt []
    results <- fetchAllRowsAL stmt

    -- fromSqlでの変更はエラーになる。
    -- "fffd" は変換に失敗した文字。
    -- (UTF8 → USC4でないので)
    let str = (fromSql (snd (head (results !! 1))))::String
    print str                        -- => "\65533I\65533R"
    print $ map (\x->showHex (ord x) "") str -- => ["fffd","49","fffd","52"]

    -- ByteString なのでSqlByteStringをはずし、
    -- Data.ByteString.putStrLnなら文字化けしない。
    print (snd (head (results !! 1))) -- => SqlByteString "\140I\142R"

    let bString = toStr (snd (head (results !! 1)))
    print bString        -- => "\140I\142R"
    B.putStrLn bString   -- => 栗山

    -- unpackをするとWord8のリストになる。
    let unpackString = B.unpack bString
    -- > :t B.unpack bString
    --  B.unpack bString :: [GHC.Word.Word8]
    print unpackString -- => [140,73,142,82]
    -- ["8c","49","8e","52"]:文字コードは SJIS。
    print $ map (\x->showHex x "") unpackString -- => ["8c","49","8e","52"]

    -- String に変換するにはリストのWord8要素をchr関数により Char型に変換。
    -- Re: [Haskell-cafe] Converting [Word8] to String
    -- http://www.mail-archive.com/haskell-cafe@haskell.org/msg09870.html
    -- > map (Char.chr . fromIntegral)
    -- > 
    -- > or
    -- > 
    -- > map (toEnum . fromEnum)
    putStrLn $ map (chr . fromIntegral) unpackString  -- => 栗山

    let ucs4 =  "あいうえお"
    print $ length ucs4              -- => 5
    print $ length $ ucs4ToSjis ucs4  -- => 10
    let ucs4 =  "123abc"
    print $ length ucs4               -- => 6
    print $ length $ ucs4ToSjis ucs4  -- => 6
    putStrLn "done"

bToString (SqlByteString s) = s

最後に disconnect を入れるとエラーになります。Haskell/Databaseの例にもdisconnectはないので、接続、切断のイメージではないのかも・・・。

Haskellでの文字コードの扱い方が分かってきた

> Prelude.putStrLn $ decodeString $ unpack $ pack $ encodeString "あいうえお"

【まとめ】SqlByteString から String への変換

  • SqlByteString を外す。
  • Data.ByteString.unpack により[Word8]に変換。
  • map (chr . fromIntegral) により Stringに変換。
  • 全角文字はsjisにすると長さが倍になり、ASCII は変化しない。(Cのイメージ)