みなさん、こんにちは。
外部プログラムとの連携その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
に複数のコマンドを与える必要があります。そんなとき、よく使う手法が起動オプションの inv
と inline
です。まず最初に 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 variable
を inv
で指定することで複数のコマンドを sqlci
に与えています。
コマンドをテキストファイルで与える場合は次のような書き方ですよね。
sqlci /in sqlrep/
in
ファイルが variable
に代わるので inv
を使う、簡単ですよね。
今回 #append built-in function
を使って sqlrep_v
にコマンドを書き足しています。#set
は上書きする function
ですので、全部 #set
にしてしまうと最後の1行だけが有効になり、それ以外のコマンドは消えてしまうので注意してください。
なお、sqlrep_v
に EXIT
コマンドが入っていませんが、in
ファイルと同じく、inv variable
を最後まで読みこむと sqlci
には eof
が渡ります。sqlci
は eof
を拾うと終了するプログラムですので、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
に対するコマンド入力として扱われています。
sqlci
を inline process
といい、@
を inline prefix
といいます。inline prefix
は inlprefix
コマンドで指定します。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)
といいます。DP
は inline
と同じようにバックグラウンドで継続して稼働してくれますが、複数プロセスを同時に起動できるのです。
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
で指定した qci
が symbolic 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
でしたね。
長くなったので続きは次回にしましょう。次回も inline
と DP
についてさらに説明していきます。 Au revoir!