読者です 読者をやめる 読者になる 読者になる

non vorrei lavorare

ブログ名の通りです。javascript three.js mruby rust OCaml golang julialang blender

mrubyでPostgres.appを使って、LOBも扱ってみた

こんばんは、次男がようやく、トイレで大を出すようになってきたと思いっていたら、またここ数日次男がウンチをしてません。kjunichiです。

背景

Qiitaで

qiita.com

という記事を見かけて、Postgres.appでも、同様にライブラリのパスを指定しないと ビルドでこけるだろうなぁと思った。

そんな訳で、アイコンがなんともエキゾチックでお気に入りのPostgres.appを思い出した。

f:id:kjw_junichi:20160614225225j:plain

mruby-pgをPostgres.appでビルドする

mruby-pgをcloneしてリンクのパスを以下の様に指定。

spec.linker.library_paths << `pg_config --libdir`.chomp

Postgres.appのpg_configを以下の様にパスに追加

export PATH=$PATH:/Applications/Postgres.app/Contents/Versions/latest/bin

他のPostgresに浮気している場合は、パスの先頭に追加する。

mruby-pgの応用編

折角、Postgres.appを使ってmruby-pgが出来たので、何かやらねばということで、 応用編にチャレンジした。

先日、顔検出や、笑顔検出に対応して、一気に機能強化したmruby-webcamで 検出した画像をネット越しに転送できないものかとあれこれ考えていたところ、 PostgreSQLのDBに格納してしまえば良いのでは?とハタと気づいた!

PostgreSQLJPEG画像を保存する

幸いmruby-webcamの画像フォーマットは現在JPEGなので、PostgreSQLに格納しても 他の画像形式よりかさばらない気がする。

まずは、PostgreSQLバイナリを扱う方法を軽く調べた。

PostgreSQLでのLOBはbytea型らしい

俺調べで、PostgreSQLbytea型を使う事で、バイナリデータを保存出来ることが判明。

以下の様に試しに画像を格納するテーブルを作成した。

CREATE TABLE image_test
(
  id serial,
  data bytea,
  lastupdate timestamp without time zone
) 

mruby-pgってLOBに対応してるのか

以下の様なコードで出来た。

# Establish connectoin
@conn = PG::Connection.new(port: 5432, dbname: "junichi")
#
# sugoi.jpgがmruby-webcamで取得した画像と仮定
data = `cat $HOME/work/sugoi.jpg`

# 画像データの保存
@conn.exec("INSERT INTO image_test(data) VALUES($1::bytea)", [{:value => data, :format => 1}])

def hex2bin(hhh)
  # 初めの2文字(00)を飛ばす。
  hhh.slice!(0,2)
  [hhh].pack "H*"
  #hhh.scan(/../).map{ |b| b.to_i(16) }.pack('C*')
  #hhh.chars.each_slice(2).map { |x| x.join.to_i(16).chr }.join
end

@conn.exec( "SELECT data::bytea FROM image_test LIMIT 1") do |res|
  # 取得したレコードは16進数の文字列で格納されている模様。
  img= hex2bin(res['data'])

  File.open("from_psql.jpg","wb") {
    |f| f.write img
  }
end

学んだこと

pg_configコマンドを知る

pkg-configのPostgreSQLの特化版といったところ。インクルードファイルのディレクトリや、 今回使ったライブラリディレクトリ等を取得できる。

cmpコマンドでバイナリファイルの比較ができる

hex2bin内の処理で先頭の2バイトを削除して対応できたのは、このコマンドのお蔭。

当初は、なぜか、DBからのレコード取得した内容をファイルに落とすと、元の画像より、1バイトサイズが 大きく、fileコマンドでは、dataファイル扱いとなってしまう謎の現象に見舞われたのですが、 このcmpコマンドと、od -cやheadコマンドで、無事、この問題を解決できました。 

mrbgemでライブラリのパスを指定する方法

spec.linker.library_pathsにパスを追加すれば、こちらのパスを優先的にリンクしてくれる模様。

関連記事