naoki86star

インターネットの片隅でなにかしら書いてみる

VoltDBのpythonスクリプトをこう書いてみている

(ここで書いた内容はいけてなくてこっちで更新してます)

今VoltDBというインメモリ型DBに携わってます。
DB操作のスクリプトを組むときにやっていることを書きます。

VoltDBというのはクセのある子(と感じてる)で、他のDBとくらべて少し違うことに気をつけておくことがあります。
DBとしての操作をするには(selectでもdeleteでも)基本的にProcedureというあらかじめの処理手順の登録が必要です。
状況とか任意のテーブルのレコード数のアドリブでの確認にコマンドラインとしてsqlcmdというのが用意されてます。*1
他のDBでdbiインタフェースを使ってスクリプト言語でさくさくというのがなかなかできにくいと感じていて、よく、たとえばmysqlとかでもpythonMySQLだいたいどのDBでもコード手順は似たものになりますけども、VoltDBの場合には、基本Procedureベースで操作することになります。pythonインタフェースも用意されてますけどプロシージャを指定して起動する、といった

VoltDBにはJsonインタフェースがあるので従来のDBIを使わずともpycurl+jsonあたりでDB操作ができるのだけれども、それも如何せんまずProcedureを準備しないといけなくて、スクリプトのメリットである一面・アドリブ的な使い方ができずにいました。

sqlcmdというコマンドはコマンドラインからqueryを受け付けてくれるのでそれを利用して、一時的にcreate procedureを作って、やりたい操作をして、最後procedureを消しておくというようなスクリプトを組むようになりました。

pythonインタフェースはvoltdbclientになっていて、以下の例だとそれを使っています。
一時procedureを作成するのにsqlcmdコマンドをsubproessで起動し、sqlcmdの--queryオプションでcreate/drop procedureを渡してます。*2
プロシージャ呼び出しはこの例はvoltdbclientを使ってますが、pycurlなりでhttpインタフェースも使えますし、そちらのほうが書く量が少ないかもしれません。*3

とにかく特にアドリブ的にVoltDBにアクセスしたいときに、同じスクリプトでプロシージャの中身を自由に編集しながら、その結果を加工処理するのをなるべくコンパクトに書きたかったのが動機です。

#!usr/bin/env python
# -*- coding:utf-8 -*-
import ipaddr
import os.path
import subprocess
import sys
import time
import traceback

from voltdbclient import *

SQLCMD="/opt/voltdb/bin/sqlcmd"
#OPTION="--output-format=csv"
QUERY="--query=%s"

def execsqlcmd(sql):

    result = subprocess.check_output([ SQLCMD, QUERY % sql ], shell=False)

    lines = result.split(b'\n')
    tuples=[]
    begin=False
    for l in lines:
        if not begin:
            if l.startswith(b'-') :
                begin = True
                continue
        else:
            if len(l)==0: break
            tuples.append([x.decode('utf-8') for x in l.strip().split()])

    return tuples

NAMETEMPLATE="tempproc"
tempprocs=[ ("select * from sample where column=?",[FastSerializer.VOLTTYPE_INTEGER]), ]

def init():
    # check sqlcmd
    if not os.path.exists(SQLCMD):
        print SQLCMD,"doesnt exist."
        sys.exit(1)

    # create procedures temporary
    for idx,s in enumerate(tempprocs):
        command = "create procedure %s%d as %s" % (NAMETEMPLATE, idx, s[0])
        execsqlcmd(command)
        print command

def close():
    for idx,s in enumerate(tempprocs):
        command = "drop procedure %s%d" % (NAMETEMPLATE, idx)
        execsqlcmd(command)
        print command

def mainloop(client):
    proc = VoltProcedure( client, NAMETEMPLATE+"0", tempprocs[0][1])
    response = proc.call( [  0 ] )
    return response

try:
    init()

    client = FastSerializer("localhost", 21212)

    response = mainloop(client)
    print response.tables

except:
    traceback.print_exc()
    pass

finally:
    close()

# end of file

*1:スクリプトjavaコマンドを呼んでいる

*2:sqlcmdは、標準入力流し込みも受け付けてくれるのでパイプでつないでもいいかもしれません。

*3:viとかで書くときVOLTなんちゃらとか定数名を毎回検索してしまうから