みなさん、こんにちは。

外部プログラムとの連携その2です。前回は fup info の結果を TACL に取り込んで編集する処理をやりました。このケースでは info コマンド一発で処理が終わりましたが、外部プログラムに複数のコマンドを与えないといけないこともよくありますよね。例えばこんな感じです。

TACL> SQL Conversational Interface - T9191H01^ACE - (10NOV08)
(C) 1987 COMPAQ (C) 2006 Hewlett Packard Development Company, L.P.

>>SET LIST_COUNT 0 ;
>>SET LAYOUT PAGE_LENGTH 10 ;
>>SELECT CLASS,LABEL,ID,NAME,NUMBER FROM SALES ORDER BY CLASS,LABEL,ID
+>WHERE DATE BETWEEN "20140401" AND "20140430" ;
>>DETAIL CLASS,LABEL,ID,NAME,NUMBER ;
>>BREAK ON CLASS,LABEL ;
>>BREAK TITLE CLASS(CLASS) ;
>>SUBTOTAL COL 5 OVER LABEL ;
>>LIST ALL ;
>>EXIT

SQL の REPORT WRITER です。この目的ではテキストファイルにコマンドを書いて obey にするか in ファイルにすることが多いと思いますが、この例をよく見ると where のところに日付が入っています。そのため、マクロで対応しないといけないようです。

このケースでは sqlci に複数のコマンドを与える必要があります。そんなとき、よく使う手法が起動オプションの invinline です。まず最初に inv を使った例を示します。

?section sqlreport macro
#frame
#push sqlrep_v dataout

#set    sqlrep_v SET LIST_COUNT 0 ;
#append sqlrep_v SET LAYOUT PAGE_LENGTH 10 ;
#append sqlrep_v SELECT CLASS,LABEL,ID,NAME,NUMBER FROM SALES ORDER BY 
CLASS,LABEL,ID
#append sqlrep_v WHERE DATE BETWEEN "%1%" AND "%2%" ;
#append sqlrep_v DETAIL CLASS,LABEL,ID,NAME,NUMBER ;
#append sqlrep_v BREAK ON CLASS,LABEL ;
#append sqlrep_v BREAK TITLE CLASS(CLASS) ;
#append sqlrep_v SUBTOTAL COL 5 OVER LABEL ;
#append sqlrep_v LIST ALL ;

sqlci /inv sqlrep_v, outv dataout/

#pop sqlrep_v dataout
#unframe

今回の注目は sqlci の起動方法です。マクロの中で起動していますが、起動パラメータに inv を指定しています。必要な sqlci コマンドは sqlrep_v variable に全部記述されていて、その sqlrep_v variableinv で指定することで複数のコマンドを sqlci に与えています。

コマンドをテキストファイルで与える場合は次のような書き方ですよね。

sqlci /in sqlrep/

in ファイルが variable に代わるので inv を使う、簡単ですよね。

今回 #append built-in function を使って sqlrep_v にコマンドを書き足しています。#set は上書きする function ですので、全部 #set にしてしまうと最後の1行だけが有効になり、それ以外のコマンドは消えてしまうので注意してください。

なお、sqlrep_vEXIT コマンドが入っていませんが、in ファイルと同じく、inv variable を最後まで読みこむと sqlci には eof が渡ります。sqlcieof を拾うと終了するプログラムですので、EXIT がなくてもプロセスは終了します。

また、sqlci の出力を dataout で拾っていますが、今回は結果を判定せずそのまま読み捨てています。前回のように dataout の内容を解析すればいいのですが(エラーが返っているかもしれません)、今回のテーマから外れるので無視しています。

では次に inline を紹介します。

?section sqlreport macro
#frame
#push #inlineprefix dataout

#set #inlineprefix @
sqlci /inline, outv dataout/

@ SET LIST_COUNT 0 ;
@ SET LAYOUT PAGE_LENGTH 10 ;
@ SELECT CLASS,LABEL,ID,NAME,NUMBER FROM SALES ORDER BY CLASS,LABEL,ID
@ WHERE DATE BETWEEN "%1%" AND "%2%" ;
@ DETAIL CLASS,LABEL,ID,NAME,NUMBER ;
@ BREAK ON CLASS,LABEL ;
@ BREAK TITLE CLASS(CLASS) ;
@ SUBTOTAL COL 5 OVER LABEL ;
@ LIST ALL ;
@ EXIT ;

#pop #inlineprefix dataout
#unframe

起動パラメータが inv から inline に代わっただけでなく、inv の場合と違うところがいくつかありますね。まず #append sqlrep_v@ に代わっています。容易に想像できると思いますが、@ で始まる行は sqlci に対するコマンド入力として扱われています。

sqlciinline process といい、@inline prefix といいます。inline prefixinlprefix コマンドで指定します。inline process にコマンドを与える時は、inline prefix の後ろに1つスペースを入れるのを忘れないでください。つまり、

@SET LIST_COUNT 0 ;
@LAYOUT PAGE_LENGTH 10 ;

ではエラーになってしまいます。

@ 自体に意味はなく、何でもかまいません。プログラムのプロンプト記号を上記のマクロをその流儀で書くなら、sqlci のプロンプトは >> なので inlprefix のところを

inlprefix >>

として

>> SET LIST_COUNT 0 ;
>> SET LAYOUT PAGE_LENGTH 10 ;

という感じということです。

また、inv の場合はコマンドを sqlrep_v に格納してから sqlci を起動していましたが、inline の場合は逆になります。起動してからコマンドを与えます。ここで間違えると、うまくいきません。

コマンド群をよく見ると、最後に EXIT コマンドがありますね。ここも inv と違うところで、明示的に終了させないとプログラムは終了しません。終了させる方法は2つあり、今回のように EXIT を指定するやり方と、inleof コマンドを打つやり方があります。inleof はプログラムに eof が渡り、プログラムがそれを拾って終了するという流れです。こちらは inv の場合と同じ流れになりますね。

視点を変えると、EXIT なり inleof なりを実施するまでは inline プロセスは終了しません。そして、inline プロセスは同時に2つは起動できません。つまり、inline プロセスを止め忘れると、次の inline プロセスは起動できないということです。

二重起動なんてしないよ、と思うでしょ?ところがけっこうやってしまうんですよ。今日の例で言うと、コマンドに雑なクエリーを書いて、データもそれなりにヤヤコシイものを用意すると、select 文がなかなか終わらないことがあります。そのように、処理に時間がかかるケースでは、EXIT を入れていても危ないんです。

処理が戻ってこない⇒待ちきれない⇒CNTL+C 押下⇒マクロ停止

とこうなると、マクロに EXIT が入っていても実行されず仕舞いです。もちろんプログラムも動いたまま。この状態でもう一度マクロを実行すると、見事に二重起動で起動エラーになってしまいます。

ということで、マクロで inline プロセスを使う場合は二重起動を検知して止めるロジックを入れたほうがいいでしょう。一例でこんな感じです。

[#if [#empty [#inineprocess]] |then|
inleof
]

と、ここまでは通常の説明ですが、そうは言っても inline プロセスを複数起動したいケースがないとは言えません。その場合はどうしたらいいでしょうか。

ここから裏技の登場です。その名を define process (DP) といいます。DPinline と同じようにバックグラウンドで継続して稼働してくれますが、複数プロセスを同時に起動できるのです。

inline では inline prefix を使ってコマンドを入力しましたが、DP ではその代わりに symbolic name を使います。まあ、能書きを垂れる前に、DP を使って前述のマクロを書き換えてみましょう。

?section sqlreport macro
dp sqlci/pname qci/

qci SET LIST_COUNT 0 ;
qci SET LAYOUT PAGE_LENGTH 10 ;
qci SELECT CLASS,LABEL,ID,NAME,NUMBER FROM SALES ORDER BY CLASS,LABEL,ID
qci WHERE DATE BETWEEN "%1%" AND "%2%" ;
qci DETAIL CLASS,LABEL,ID,NAME,NUMBER ;
qci BREAK ON CLASS,LABEL ;
qci BREAK TITLE CLASS(CLASS) ;
qci SUBTOTAL COL 5 OVER LABEL ;
qci LIST ALL ;

undp qci

どうですか、inline とほぼ同じですね。1番最初で sqlci を起動しています。

dp sqlci/pname qci/

頭に dp とついているのがミソで、これによって DP プロセスが起動されます。pname で指定した qcisymbolic name です。inline prefix と違って symbolic name は記号は指定できません。マクロの命名規約と同じ命名規約が適用されます。実は起動時に裏で symbolic name と同名のマクロが作成されますので、ここで他のマクロと同じ名前、例えば fileinfo など、を指定してはなりません。

こうすれば複数のプロセスを同時に扱えるのですから、お得感満載ではありませんか。でも目ざとい人は「ん?そのマクロ、なんかおかしくない?」と思っているかもしれませんね。そう outv がありません。気がつきましたか。そこは次回に説明を廻します。今回は outv の話をして終わりにしたいと思ってます。

outv ですが、実は DP の場合、起動オプションに outv が指定できません。DP の起動オプションは TACL 上での起動オプションとは別物で、TACL の起動オプションは半分くらいは使えません。outv も使えないうちのひとつでDP プロセスは結果を直接画面に出してしまうのです。だとすると DP プロセスはマクロと連携できないのです。

それではここで紹介した意味がありません。で、こんな風に修正します。

?section sqlreport macro
#frame
#push dataout
dp sqlci/pname qci,nowait/

qci SET LIST_COUNT 0 ;
qci SET LAYOUT PAGE_LENGTH 10 ;
qci SELECT CLASS,LABEL,ID,NAME,NUMBER FROM SALES ORDER BY CLASS,LABEL,ID
qci WHERE DATE BETWEEN "%1%" AND "%2%" ;
qci DETAIL CLASS,LABEL,ID,NAME,NUMBER ;
qci BREAK ON CLASS,LABEL ;
qci BREAK TITLE CLASS(CLASS) ;
qci SUBTOTAL COL 5 OVER LABEL ;
qci LIST ALL ;

wait/outv dataout/qci

undp qci
#pop dataout
#unframe

ご覧のとおり、nowait オプションを追加しました。こうすると画面に出さずに TACL の内部に保留するようになります。その上で、最後に wait コマンドで dataout variable に取り出しています。これで無事 DP プロセスの出力をマクロに取り込むことができました。

注意してほしいのですが、この nowait オプションは、みなさんが知っている nowait オプションとは別物です。どういう意味かは次回解説したいと思います。

なお、DP には outv オプションもなければ out オプションもないので、前回のように out オプションで一旦ファイルに吐き出してから variable に取り込む技は使えません。nowait オプションで variable に取り込んでしまえば、variable からファイルに吐き出すことはもちろんできます。vartofile でしたね。

長くなったので続きは次回にしましょう。次回も inlineDP についてさらに説明していきます。 Au revoir!