IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

Haskellでのデータベースアクセス

PostgresqlのHDBCを利用しました。

HDBC: Haskell Database Connectivity

作業場所の作成

$ mkdir haskelldatabase
$ cd haskelldatabase

サンドボックスの作成

$ cabal sandbox init
Writing a default package environment file to
/Users/bar/work03/haskelldatabase/cabal.sandbox.config
Creating a new sandbox at
/Users/bar/work03/haskelldatabase/.cabal-sandbox

HDBCのインストール

$ cabal install hdbc
Resolving dependencies...
Notice: installing into a sandbox located at
/Users/bar/work03/haskelldatabase/.cabal-sandbox
Configuring mtl-2.2.1...
Configuring old-locale-1.0.0.7...
Downloading utf8-string-1.0.1.1...
Configuring text-1.2.2.1...
Configuring utf8-string-1.0.1.1...
Building old-locale-1.0.0.7...
Building mtl-2.2.1...
Building text-1.2.2.1...
Building utf8-string-1.0.1.1...
Installed old-locale-1.0.0.7
Configuring old-time-1.1.0.3...
Building old-time-1.1.0.3...
Installed mtl-2.2.1
Installed utf8-string-1.0.1.1
Installed old-time-1.1.0.3
Installed text-1.2.2.1
Downloading convertible-1.1.1.0...
Configuring convertible-1.1.1.0...
Building convertible-1.1.1.0...
Installed convertible-1.1.1.0
Downloading HDBC-2.4.0.1...
Configuring HDBC-2.4.0.1...
Building HDBC-2.4.0.1...
Installed HDBC-2.4.0.1
Updating documentation index
/Users/bar/work03/haskelldatabase/.cabal-sandbox/share/doc/x86_64-osx-ghc-8.0.1/index.html

postgresqlドライバのインストール

$ cabal install hdbc-postgresql
Resolving dependencies...
Notice: installing into a sandbox located at
/Users/bar/work03/haskelldatabase/.cabal-sandbox
Configuring parsec-3.1.11...
Building parsec-3.1.11...
Installed parsec-3.1.11
Downloading HDBC-postgresql-2.3.2.4...
Configuring HDBC-postgresql-2.3.2.4...
Building HDBC-postgresql-2.3.2.4...
Installed HDBC-postgresql-2.3.2.4
Updating documentation index
/Users/bar/work03/haskelldatabase/.cabal-sandbox/share/doc/x86_64-osx-ghc-8.0.1/index.html
$ 

データベースの準備

$ createuser -a -d -U bar -P
user03
Enter password for new role: 
Enter it again: 
$ createdb -E UTF8 -O user03 -U user03
testdb03

データベスの確認

$ psql -l
                                     List of databases
   Name    |   Owner    | Encoding |   Collate   |    Ctype    |     Access privileges     
-----------+------------+----------+-------------+-------------+---------------------------
 testdb03  | user03     | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | 

テーブルの作成

$ psql -d testdb03
psql (9.6.1)
Type "help" for help.

testdb03=# 
testdb03=# create table foo(name varchar(50));


testdb03=# \dt;
         List of relations
 Schema | Name | Type  |   Owner    
--------+------+-------+------------
 public | foo  | table | bar
(1 row)

テーブル情報の確認

> :t connectPostgreSQL
connectPostgreSQL :: String -> IO Connection
> :t getTables 
getTables :: IConnection conn => conn -> IO [String]
> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= (\x -> getTables x)
["foo"]
> 

データ挿入、オートコミットはされない。

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= (\x -> run x "insert into foo values('abc')" [])
1
>

データ挿入+コミット

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= (\x -> run x "insert into foo values('ghi')" [] >>= (\_ -> commit x))
>

検索(prepare -> execute -> fetch->結果はMaybeというパターン)

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo" >>= (\y -> execute y [] >>= \_ -> fetchRow y)
Just [SqlByteString "abc"]

列名付き検索

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo" >>= (\y -> execute y [] >>= \_ -> fetchRowAL y)
Just [("name",SqlByteString "abc")]

列名付き検索をMapで取得

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo" >>= (\y -> execute y [] >>= \_ -> fetchRowMap y)
Just (fromList [("name",SqlByteString "abc")])

複数行取得

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo" >>= (\y -> execute y [] >>= \_ -> fetchAllRows y)
[[SqlByteString "abc"],[SqlByteString "def"],[SqlByteString "ghi"]]

検索結果をMapで取得

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo" >>= (\y -> execute y [] >>= \_ -> fetchAllRowsMap y)
[fromList [("name",SqlByteString "abc")],fromList [("name",SqlByteString "def")],fromList [("name",SqlByteString "ghi")]]

日本語の取り扱い

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= (\x -> run x "insert into foo values('あいうえお')" [] >>= (\_ -> commit x))
> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo" >>= (\y -> execute y [] >>= \_ -> fetchAllRowsMap y)
[fromList [("name",SqlByteString "abc")],fromList [("name",SqlByteString "def")],fromList [("name",SqlByteString "ghi")],fromList [("name",SqlByteString "\227\129\130\227\129\132\227\129\134\227\129\136\227\129\138")]]

NULLの取り扱い SqlNullというデータがある

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> prepare x "select * from foo where name is null" >>= (\y -> execute y [] >>= \_ -> fetchAllRowsMap y)
[fromList [("name",SqlNull)]]

トランザクションを使う

> connectPostgreSQL "host=localhost dbname=testdb03 user=user03" >>= \x -> withTransaction x (\y -> run y "insert into foo values('klm')" [])
1

do記法

import Database.HDBC 
import Database.HDBC.PostgreSQL

main = do 
  con <- connectPostgreSQL "host=localhost dbname=testdb03 user=user03"
  run con "insert into foo values('qwerty')" []
  commit con

Blenderで立方体を床に落とすサンプル

Cubeが床に落ちることをやります。

f:id:naotoogawa:20161225124915p:plain

1: キューブを追加する(Shift+A -> Mesh -> Cube)。

f:id:naotoogawa:20161225124514p:plain

2: Cubeを上に動かす。

f:id:naotoogawa:20161225124524p:plain

3: Physics -> Rigid Body -> Type : Active とする。

f:id:naotoogawa:20161225124533p:plain

4: 床を追加する(Shift+A -> Mesh -> Plane)。

f:id:naotoogawa:20161225124546p:plain

5: Planeの位置と大きさを調整する。

f:id:naotoogawa:20161225124558p:plain

6: Physics -> Rigid Body -> Type : Passive とする。

f:id:naotoogawa:20161225124608p:plain

7: 再生する

f:id:naotoogawa:20161225124617p:plain

8: キューブが床に落ちる

f:id:naotoogawa:20161225124625p:plain

waiでのrequestの処理

waiでrequestの基本的な使い方を確認するサンプル。

wai_hello.hs

wai_index.html

*Main> main
start app function ====================================
requestMethod  : GET
secure         : False
rawPathInfo    : /cgi.php
rawQueryString : ?foo=1111
requestHeaders : 
host : 127.0.0.1:3030
accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
accept-language : ja-jp
connection : keep-alive
accept-encoding : gzip, deflate
user-agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4
queryString    : 
foo : 1111
start app function ====================================
requestMethod  : POST
secure         : False
rawPathInfo    : /cgi.php
rawQueryString : ?foo=111&bar=222
requestHeaders : 
host : 127.0.0.1:3030
content-type : application/x-www-form-urlencoded
origin : file://
connection : keep-alive
accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
user-agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4
accept-language : ja-jp
accept-encoding : gzip, deflate
content-length : 7
queryString    : 
foo : 111
bar : 222

map, map, liftM

> :t map 
map          ::                  (a  -> b) -> [a]  -> [b] 

> :t fmap
fmap         :: Functor f     => (a  -> b) -> f a  -> f b 

> :t liftM
liftM        :: Monad m       => (a1 -> r) -> m a1 -> m r 

> :t (<*>) . pure
(<*>) . pure :: Applicative f => (a  -> b) -> f a  -> f b 

> map            (+1) [1,2,3]
[2,3,4]

> fmap           (+1) [1,2,3]
[2,3,4]

> liftM          (+1) [1,2,3]
[2,3,4]

> ((<*>) . pure) (+1) [1,2,3]
[2,3,4]
> 

3分でHaskellのWebアプリ

のサンプルを動かします。但し、待ち時間を除きます。

Beginning Haskell - A Project-Based Approach | Alejandro Serrano Mena | ApressのWebアプリの説明では、フレームワークとしてscottyを使っているので、導入してみました。

バージョンの確認

webapp $ cabal -version
cabal: unrecognised command: -version (try --help)
webapp $ cabal --version
cabal-install version 1.24.0.0
compiled using version 1.24.0.0 of the Cabal library 
webapp $ cabal repl
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
Prelude> :q
Leaving GHCi.

作業場所の作成

webapp $ mkdir scotty02
webapp $ cd scotty02
scotty02 $ ls
scotty02 $ cabal sandbox init
Writing a default package environment file to
/Users//work03/webapp/scotty02/cabal.sandbox.config
Creating a new sandbox at
/Users//work03/webapp/scotty02/.cabal-sandbox

scottyのインストール

scotty02 $ cabal install scotty
Resolving dependencies...
Notice: installing into a sandbox located at
/Users//work03/webapp/scotty02/.cabal-sandbox
Downloading ansi-terminal-0.6.2.3...
Downloading appar-0.1.4...
Configuring base64-bytestring-1.0.0.1...
Configuring base-compat-0.9.1...

途中省略

Installed wai-extra-3.0.19
Downloading scotty-0.11.0...
Configuring scotty-0.11.0...
Building scotty-0.11.0...
Installed scotty-0.11.0
Updating documentation index

サンプルアプリケーションの作成

scotty02 $ cat hello.hs 
{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty

main :: IO ()
main = scotty 3010 $ do
  get "/" $ do
    html "Hello, Scotty World!"

起動

scotty02 $ cabal exec runghc hello.hs 
Setting phasers to stun... (port 3010) (ctrl-c to quit)

Webアクセス

client$ curl http://localhost:3010/
Hello, Scotty World!
client$