2013年11月26日火曜日

Golang Cafe #5を開催しました。

Golang Cafe #5を開催しました。

今回は、osパッケージを利用するのをテーマに進めました。
githubのリポジトリにもpush済みですので、ソースコードはそちらをご覧ください。

私が動作させた関数は以下です。

  • Open
  • Getenv
  • IsExist
  • IsNotExist
  • Chdir
  • Remove
  • RemoveAll
  • Mkdir
  • MkdirAll
  • Environ
まず、注意点として、パスを指定する時に"~"の指定はできないので、ホームディレクトリを取得する場合は、Getenv("HOME")で環境変数から取得して下さい。
ちなみに、"~"を使うと、カレントディレクトリに"~"というディレクトリができて困ることになります。

ディレクトリの存在チェック
os.Open()を使います。ディレクトリが存在しない場合は、戻り値にerrorが返されるので、直接errorを判定してもいいですが、IsExist()とIsNotExist()を呼ぶと、true/falseで存在チェックができるようになっているので、そちらを利用する方が便利です。

RemoveAllとRemove、MkdirAllとMkdirの違いですが、複数階層のディレクトリを指定する場合は、RemoveAll()、MkdirAll()を呼び出さないと、ディレクトリの作成、削除はできません。(errorが返されます)存在するディレクトリに新たにディレクトリを作る/削除する場合は、Mkdir()、Remove()でも実行できます。

Chdir()はカレントディレクトリを移動します。

Environ()は環境変数名の一覧を取得する関数です。Getenv()の引数もEnviron()で取得できる文字列を指定すれば、環境変数の設定を取得する事ができます。

それから、今回はWindowsでの動作が「サポートされていない」という状況が多かったのでWindowsで動作を検証していた+Takanobu Haginoさんは、ちょっとお困りのようでした。
Windowsにはuidなどの属性が存在しないのでGetuid()などを呼び出しても-1しか返ってきません。実装がreturn -1になっています。(syscallパッケージのソースコードに書かれています)(これはWindowsの仕組み上、どうしようもないと思われる)

osパッケージは内部でsyscallパッケージの関数を呼び出すようになっている事が多いので、syscallパッケージを触ってみる方が良いのかもしれません。

ということで、今回はあまり動かせなかったので、また今度osパッケージを使う(2回目)が行われることでしょう。

次回の#6はtemplateパッケージ(私は最近使わなくなってしまった…)の使い方がテーマになる予定です。

2013年11月18日月曜日

Golang Cafe #4を開催しました。

Golang Cafe #4を開催しました。

今回のテーマは、「database/sqlパッケージを触る」ということで、
事前にPostgreSQL9.3.1をHomebrewでインストールしておきました。

リポジトリはGithubにあります。https://github.com/tyokoyama/golangcafe
ブランチCafe_#4を作成していますが、masterブランチにもマージしていますし、ディレクトリを変えているのでmasterからも参照できます。

PostgreSQLを使うので、ライブラリはPure Goのgithub.com/lib/pqを利用しました。
(他のデータベース、ライブラリを利用される方は、こちらを参照して下さい)

導入方法は、
go get github.com/lib/pq
で、インストールできます。

ここからは、lib/pqの前提で説明を続けます。

まず、クライアントの文字コードをUTF-8にしておく必要があります。(チェックロジックがpq/conn.goの113行目にあります。恐らく、新しいPostgreSQLならdefaultがUTF-8になっているので大丈夫だろうと思われます。

まず、pqのインポートを行って下さい。
import (
 _ "github.com/lib/pq"
 "database/sql"
)

import時に"_"をつけると、パッケージの読み込みは行うが、プログラムでは呼び出さない事を明示しています。今回は、database/sql/driverパッケージがinterfaceのかたまりで実装はpqにあります。したがって、database/sqlパッケージがdatabase/sql/driverのインターフェイスを呼び出すことで、pqで実装されたPostgreSQLへのクエリ実行のコードが呼び出される仕組みになっています。
要は、database/sqlがpqのドライバを呼び出すのでimportしておかないと、panicになると覚えておけば良いと思います。

データベースへの接続
db := sql.Open("postgres", "user=gdgchugoku dbname=sampledb sslmode=disable")
defer db.Close()

pqのドキュメント通りではないですが、PostgreSQL側のsslの設定を有効にしておかないとsslmode=verify-fullが動かないのと、省略時がsslmode=requiredらしいので、標準でsslを使った接続になるようです。したがって、サンプルで試す時はsslmode=disableにしてsslを使わないように指定しましょう。

Queryの実行
公式のサンプルだと、
row := db.QueryRow("select * from character where cost=?", param)
などと、記述できるようですが、
pqを利用する場合は、
row := db.QueryRow("select * from character where cost=$1", param)
というように、?ではなく、$1などという指定しか受け付けないようです。
http://godoc.org/github.com/lib/pqを見る限りでも、?のサンプルが見当たらないので、サポートしていないのかもしれません。コードを書く時は、Driver側のドキュメントを見た方がいいかもしれません。
ちなみに、RowもRowsもClose()が実装されていますが、自動的にクローズされるということなので、明示的に呼び出す必要はありません。
RowとRowsの違いですが、Rowの方は先頭1行のみで、全行取得はRowsを利用します。(中身の実装はRowもRowsを含んでいるので、同じものです。便利機能というところでしょうか?)

カーソルの制御
db.Query()の戻り値の*Rows.Next()を呼び出し、カーソルを次の行に移動させます。Golangの(pqの?)カーソルは最初の読み出しでもNextを呼び出さないと、行のデータが取得できません。
サンプル通りですが、以下の様なコードになります。
 if rows, err := db.Query("select * from character"); err == nil {
  if names, err := rows.Columns(); err != nil {
   t.Errorf("row.Columns error %v", err)
  } else {
   for name := range names {
    t.Logf("name = %s", name)
   }
  }

  for rows.Next() {
   // 行の中身を見る。
   var no int
   var name string
   var cost float64
   var typeId int
   var attack int
   var lead int
   var scheme string
   var morale int

   if err := rows.Scan(&no, &name, &cost, &typeId, &attack, &lead, &scheme, &morale); err != nil {
    t.Errorf("Scan Error %v", err)
   }
   t.Logf("Rows[%d, %s, %f, %d, %d, %d, %s, %d]", no, name, cost, typeId, attack, lead, scheme, morale)
  }
 } else if err == sql.ErrNoRows {
  t.Logf("Query NoRows")
 } else {
  t.Errorf("Query error %v", err)
 }

*Rows.Columns()は列の名前を取得するメソッドですが、PostgreSQLの仕様なのか、0,1,2・・・という数字(列番号)しか返してきませんでした。select * from ...のように全列取得だからかと列指定もしてみましたが、同様に0,1なので、他言語で列名が取得できるのは、何か別の工夫があるのか、pqの実装がショボイのかはわかりませんでした。(Driverの開発ノウハウが必要かも?)

更新系のクエリを実行する場合ですが、参照系と同様に、*DB.Query()を使えば実行できます。また、*DB.Exec()もあるので、そちらを利用してもいいかもしれません。ただ、pqの実装が甘いようで、Exec()だと、更新クエリにより影響を受けた行数はResult.RowsAffected()で取得できますが、最後に追加したIDはAPIとしては、Result.LastInsertId()が用意されていますが、pqではサポートしていません。(ドキュメントにも明記されています)

  if row, err := db.Query(fmt.Sprintf("insert into character values(%d, '羽柴秀吉', 2.5, 1, 8, 6, '撹乱貫通射撃', 4) RETURNING no", no)); err != nil {
   t.Errorf("Insert Query Error %v", err)
  } else {
   if names, err := row.Columns(); err != nil {
    t.Errorf("row.Columns error %v", err)
   } else {
    for name := range names {
     t.Logf("name = %s", name)
    }
   }

   row.Next()
   if names, err := row.Columns(); err != nil {
    t.Errorf("row.Columns error %v", err)
   } else {
    for name := range names {
     t.Logf("name = %s", name)
    }
   }

   if err := row.Scan(&no); err != nil {
    t.Errorf("Scan after insert Error %v", err)
   }
   t.Logf("no = %d", no)
  }

LastInsertId()がサポートされていないので、現状で更新したIDを取得するためには、PostgreSQLのクエリの機能であるRETURNINGをつける必要があります。これは、更新、削除の時も利用できますが、UPDATEの時に指定すると更新された行全てが返ってくるようになっています。(試していないけど、DELETEの時も複数行返ってくると思われる)

と、ここまでがトランザクションを使わずCRUDを実現する方法でした。
量が多いので、別の記事にしようと思っていますが、ひとまず、Golang Cafe #4のまとめとしてはこの辺で終わります。

#4で議論になったこと。
Q. GolangにORM無いの?
紹介がありました。
http://jmoiron.net/blog/golang-orms/


2013年11月11日月曜日

Mac OS X 10.9(Marvericks)にPostgresqlをインストールする。

Mac OS X 10.9にPostgresqlをインストールしようとしたら、かなり苦戦したのでメモしておく。

まず、homebrewを使ってインストール。

brew install postgresql

とすると、PostgreSQL 9.3.1をインストールしようとするが、tcl-tkがないと言われるので、

brew install tcl-tk

その後、

brew install postgresqlする。

それでも、以下のエラーになるので、

configure: error: file 'tclConfig.sh' is required for Tcl

issueの対策を施して
args << "--with-tcl" unless build.include? 'no-tcl'の行の次の行に
args << "--with-tclconfig=/usr/local/Cellar/tcl-tk/8.6.0/lib" unless build.include? 'no-tcl'を追加

brew install postgresql

でも、まだplperl.soのエラーがでるので、

xcode-select --install
で、コマンドラインツールをインストールして、

brew install postgresql

で、インストール完了!

psql -Vの結果が
psql (PostgreSQL) 9.3.1

となっていたので、大丈夫なんだろう。とりあえず、PostgreSQLをあまり使っていないので、そこからだな。

Golang Cafe #3 を開催しました。

Golang Cafe #3を開催しました。

今日のお題はGoroutinesとchannelを使った並列プログラミング(初級)という感じです。githubにコードを置いてあります。

ワーカースレッド(goroutine)を起動するには、呼び出し時にgoを記述する。

Channelを使って、簡単な排他制御ができる。
ch := make(chan int)でChannelを作る。
ch<-1だと、Channelに1を送る。<-chだと、Channelから値を受け取る。

ちょっと、サンプルコードのミスで、よくわからないコードができてしまったが、コンパイルエラーにならないのが不思議だった。該当のソースコードはこちら

break、continueの使い方
言語仕様を参照。基本的にラベルの次のfor、switch、selectを抜ける。
continueはラベルの次のforをもう一度実行。

selectについて。言語仕様を確認した感じ。
select文にdefaultを入れると、defaultを呼び出して抜けてしまう。ただし、defaultがなければ、caseのどれかが呼ばれるまで処理をブロックする。

select{}にすると、無限に待ち続ける。

time.Tick()とtime.After()を同時に呼び出して、selectで同時に待ち受けるとtime.After()が永久に戻ってこない?(ただし、time.TickでのChannelの応答の前に、time.After()のタイミングがあると、呼び出される)
バグ…?
と思っていたら、+Fumitoshi Ukaiさんから、正しい書き方を教えていただきました!ありがとうございました。

runtime.GOMAXPROCS(n)を呼び出すと、CPUの利用数が設定できる。(戻り値は前の設定なので注意!)これを呼び出すと、goroutinesが並列に処理されるようになる。
が、アクティビティモニタで確認したら、スレッド数は6だった。(2を期待したけど、Go言語ランタイム(?)の方のスレッドがいるんだろうか。)

関数の引数で、channelが「送信専用」か「受信専用」かを明示することができる。
送信専用=ch chan<- int
受信専用=ch <-chan int
間違いがあると、コンパイル時にエラーになる。

次回は、database/sqlパッケージを使って、DBにアクセスする回になりそうです。

2013年11月5日火曜日

MacOS X Marvericksはまだ早い!

無料でアップデートできるという、噂のMac OS X Marvericksですが、無料なので、私も早速アップデートしてみました。

が、いろいろなアプリケーションが動かなくなったり、動きが悪くなってきたりしているので、今後、アップデートを検討している方へのメモとして残しておこうと思います。

ちなみに、購入したのは2012/12/31で、Core i7 2.6GHz、メモリ8GBの比較的新しいモデルだと思います。

動かなくなったもの

  • Mercurial
動きがわるくなったもの
  • VirtualBox
  • Chrome(?)
  • homebrew
  • Mac OS自体の動作。ログインとか、ロック解除とかがもっさり…。
MercurialはMacのPythonのパスが変わったということで、動作しなくなったようですが、homebrewからのビルドが通らなくなってしまって(XCodeの利用規約の承認が必要だった?)結局、sudo easy_install -U mercurialで解決しました

VirtualBoxに至っては、一度起動したら、毎回セットアップからインストールしないと
まともに起動しないという状態に陥っていて、仕事でVirtualBoxをフル活用している私としてはかなり悩ましい問題になっています。最新バージョンの4.3.0なのに…。
早く改善して欲しいものです。

ということで、今のところ、問題となることが多いようなので、Mountain Lionで様子を見ている方はそのまま様子を見た方がいいと思います。

私だけが特別ならいいんだけど…。

2013年11月3日日曜日

Golang Cafe #2を開催しました。

先週に引き続き、Golang Cafe #2を開催しました。
今回は、encoding/jsonパッケージのMarshal()から読み始めました。

それまでに、前回のExampleの事を復習していたら、godocコマンドのハックに…。
(ですが、本題と違ったのでまた違う日にやることにしました)

色々出てきたのでメモ。
引数の型がinterface{}になる時は、「何でもOK」という意味。(他の言語で言う、Object、object、void *、variant…など。)

deferキーワード。呼び出した関数が終了した後に呼び出される、後処理用スタックに積む。(その場で処理は実行されないので、ファイルのクローズなどをまとめておくと良い。)

recover()はdeferキーワードを使って呼び出した関数内でしか利用できないので注意。

reflect.Valueは、今日は読まないが、「ポインタを持った構造体」
reflect.ValueOf()はポインタを持った構造体を返す関数ということにしておく。

読んだ所は、encoding/json#Marshalを掘り下げる感じで。

  1. encoding/json#Marshal
  2. e = &encodeState{}
  3. e.marshal()
    1. エラーチェック用defer関数を登録して、e.reflectValue()
    2. reflectValueQuoted(v, false)で呼び出す。(1.1.2だと267行目付近)
      1. IsValid()かどうかをチェック
      2. Marshalerインターフェイスを実装しているかどうかをチェック(TypeAssertionを使って、変換する)
      3. Marshalerインターフェイスではない場合、ポインタをもらっているという想定で、v.Addr().Interface().(Marshaler)で参照先の値を取り出してTypeAssertionする。
        1. TypeAssertionは変換できない場合もあるので、代入用変数と、チェック用の変数を2つ左辺に記述する形で使う。(i, ok := v.(int)…など)チェック用変数がfalseの場合は変換できなかった事を示す。(interfaceを実装していない、型が違うなど。)
        2. MarshalJSON()([]byte, error)を実装していれば、自分で実装したJSONエンコード処理が呼ばれる。
      4. それ以外は、reflect.Kind()で型の種類を取得して、それぞれの方法で出力する。
        1. 構造体はフィールドごとに再起呼び出し。
        2. fallthroughキーワードは次のcaseを実行する。
          1. Goのswitchはデフォルトbreak!
        3. reflect.Kind()はhttp://goo.gl/uDQOPOのconst値を返してくる。
        4. invalid、unsafePointer、channel、func、Mapのキーがstring以外のものは、変換できずerrorを返す。

それから、私が作成したサンプルコードとテストコードをまとめてgithubに置きました。
https://github.com/tyokoyama/golangcafe
次からはこのリポジトリにまとめてCommitしていくことにします。
(忘れてなかったら、各回ごとにbranchも作っておくようにします。)

次回は、goroutine & channels関連をやってみる予定です。