
本記事は、前回に引き続きマクロについて解説致します。
システム・プリプロセッサ・コマンド
マクロで使用するコマンドを一覧にしました。
サンプルも添えているので、使い方等に迷ったら参考にして下さい。
メイン
#defin
マクロの基本
下記記事で紹介したので割愛します。
#def1Arg
引数を1つだけ設定が可能で、引数に「,」を含ませることが可能
ClassMethod testSample()
{
#def1Arg TEST1(%arg) w !,%arg
#def1arg clsMth(%subs) ##expression("##class("_$li(%literalargs,1)_")."_$li(%literalargs,2)_"("_$lts($li(%literalargs,3,*),",")_")")
$$$TEST1("a","b","c")
w $$$clsMth("developer.macro.Sample","WriteCmnt",1,2)
}
コンパイル結果
testMacro() methodimpl {
w !,"a","b","c"
w ##class("developer.macro.Sample")."WriteCmnt"(1,2) }
便利系
#include
インクルードファイルをロードする。
使い方は「Include」と変わらないです。
#dim
「#dim」を使い変数の型定義を行うと、関数やパラメータがインテリセンスとして表示されます。
ClassMethod testSample()
{
#dim e as %Exception.AbstractException
try{
}catch e{
w e.DisplayString()
}
}
#execute
コンパイル時に ObjectScript の行を実行する。
下記サンプルは、コンパイル日時をグローバル「^sample.compile」に設定する。
ClassMethod testSample()
{
#Execute s ^sample.compile = $zdt($h,3,1)
}
補助的なマクロ
##continue
マクロを複数行に渡って記述する際に利用します。
#define SQLGO(%rset, %query, %param) ##continue
s stmt = ##class(%SQL.Statement).%New(1) ##continue
s %rset = stmt.%ExecDirect(, %query, data...) ##continue
##expression
コンパイル時に式を評価します。
ClassMethod testSample()
{
#define dSec1 60*60*24
#define dSec2 ##Expression(60*60*24)
w $$$dSec1
w $$$dSec2
#define now ##Expression("""一日の秒数"_$$$dSec2_"秒""")
w !,$$$now
}
コンパイル結果
testSample() methodimpl {
w 60*60*24
w 86400
w !,"一日の秒数86400秒" }
##function
コンパイル時に関数を評価します。
ClassMethod testSample()
{
#define dSec ##Function(##class(developer.macro.Sample).retVal())
w $$$dSec
}
ClassMethod retVal()
{
q 60*60*24
}
コンパイル結果
testSample() methodimpl {
w 86400 }
##unique
マクロ定義内で、一意の新規ローカル変数を作成する。
##Unique(new)と##Unique(old)は対となっていて、「new」で格納した値を「old」で返す。
set ##Unique(new) = [値]
set [変数] = ##Unique(old)
ClassMethod testSample(int As %Integer)
{
#define SAMPLE(%from, %to) s ##Unique(new) = %from, to = ##Unique(old)
#define SET(%arg) s ##Unique(new) = %arg * 10
#define GET ##Unique(old)
$$$SAMPLE(int, to)
w to,!
$$$SET(10)
w $$$GET
}
コンパイル結果
testSample(int) methodimpl {
s %mmmu1 = int, to = %mmmu1
w to,!
s %mmmu2 = 10 * 10
w %mmmu2 }
コンパイル時の判定とマクロの削除
#If, #Else, #ElseIf, #EndIf
「#IF, #Else, #ElseIf」で引数の条件を判定し、trueの場合に該当のブロックをコンパイルします。
下記サンプルは、コンパイル時の「$$$SAI」の値によって、変数「val」の値が確定し出力します。
変数「val」の値は、コンパイルするまで変更されません。
ClassMethod testSample()
{
#define SAI $r(6)+1 ##;サイコロの目
#If $$$SAI=1
s val = "1の目"
#ElseIf $$$SAI=2
s val = "2の目"
#ElseIf $$$SAI=3
s val = "3の目"
#ElseIf $$$SAI=4
s val = "4の目"
#ElseIf $$$SAI=5
s val = "5の目"
#Else
s val = "6の目"
#EndIf
w !,val
}
コンパイル結果
testSample() methodimpl {
s val = "5の目"
w !,val }
#IfDef, #IfNDef
コンパイル時、マクロが存在している/していないの判定で、実行されるブロックが変わります。
「#IfDef」マクロが存在している場合trueとなる
「#IfNDef」マクロが存在していない場合trueとなる
ClassMethod testSample()
{
#define Exi
w !,"マクロがあるのか?"
#IfDef Exi
w "ある!"
#Else
w "ない!"
#EndIf
}
コンパイル結果
testSample() methodimpl {
w !,"マクロがあるのか?"
w "ある!"
}
ClassMethod testSample()
{
w !,"マクロがあるのか?"
#IfDef Exi
w "ある!"
#Else
w "ない!"
#EndIf
}
コンパイル結果
testSample() methodimpl {
w !,"マクロがあるのか?"
w "ない!"
}
#undef
定義されているマクロの定義を削除する。
ClassMethod testMacro()
{
#define TEST w !,"sample"
#undef TEST
#ifNDef TEST
s txt = "未定義"
#else
s txt = "定義済"
#endif
w txt
}
コンパイル後
testMacro() methodimpl {
s txt = "未定義"
w txt }
コメント系
#;
intファイルに反映されないコメントを作成します。
ClassMethod testSample()
{
#; 表示されない
w !,表示される
}
コンパイル結果
testSample() methodimpl {
w !,表示される }
##;
「##;」以降の文字列は、intファイルに反映されない。
ClassMethod testSample()
{
w !,表示される ##; 表示されない
}
コンパイル結果
testSample() methodimpl {
w !,表示される }
#show, #noshow
#Show ~ #NoShowまでの間のコメントは、参照元に表示される。
インクルードファイルに下記コメントを記載すると・・・
// 範囲外のコメント前
#Show
// 範囲内のコメント
// 範囲内改行
#noShow
// 範囲外のコメント後
// 改行!
INTファイルに下記文言が表示されます。
// 範囲内のコメント
// 範囲内改行
INTファイルに出力されるのか。
普段、あまり見ないファイルに出力されてもなぁ・・・勿体ない。
文字列操作系
##quote
引数に対し、引用符を付けて返す。
引数の中に「”」があると、エスケープして返す。
ClassMethod testSample()
{
w !,##Quote(藤原道長)
w !,##Quote(娘の名前は"彰子"です。)
}
コンパイル結果
testSample() methodimpl {
w !,"藤原道長"
w !,"娘の名前は""彰子""です。"
}
##quoteExp
コンパイル時に評価される式を引数として取る。
ClassMethod testMacro()
{
s ^sample("test","data","sample")="でます"
#def1arg gbl(%subs) ^sample("test"##expression($s(%literalargs'=$lb(""):","_$LTS(%literalargs,","),1:"")))
#def1arg gblStr(%subs) ##quoteExp($$$gbl(%subs))
w $$$gbl("data","sample"),!
w @$$$gblStr("data","sample")
}
コンパイル結果
testMacro() methodimpl {
s ^sample("test","data","sample")="でます"
w ^sample("test","data","sample"),!
w @"^sample(""test"",""data"",""sample"")" }
##beginquote ~ ##EndQuote
引用符を付与する。「”」があるとエスケープする。
公式ドキュメントのサンプルにある「##EndQuote」は「##endquote」に書き換えると動作します。
ClassMethod testMacro()
{
s a=##beginquote 紫"式"部 ##endquote
s b=##beginquote SET def="SQL code-generation" &SQL(SELECT Name ##endquote
}
コンパイル結果
testMacro() methodimpl {
s a=" 紫""式""部 "
s b=" SET def=""SQL code-generation"" &SQL(SELECT Name "
}
・・・前後に半角スペースが入りますね。
恐らく、##beginquoteと##endquoteの間の半角スペースです。
なので、半角スペースを削ると、赤い波線が表示されますが・・・

testMacro() methodimpl {
s a="紫""式""部"
s b=" SET def=""SQL code-generation"" &SQL(SELECT Name "
}
コンパイル後のINTファイルから、半角スペースが消えました。
うん。
使いづらい。
括弧で括ると、赤い波線が消えますが・・・

testMacro() methodimpl {
s a="(紫""式""部)"
s b=" SET def=""SQL code-generation"" &SQL(SELECT Name "
}
コンパイルすると当然括弧が付きます。
「xecute」実行用として、関数を文字列で構築する時に使えるかも?
##stripq
引用符を削除してその引数を返す。「##quote」とは逆の動き
ClassMethod testMacro()
{
s str = "サンプル"
#define TEST(%arg) ##stripq(%arg)
w $$$TEST("str")
}
コンパイル後
testMacro() methodimpl {
s str = "サンプル"
w str }
SQL関連
#SQL
実行時にSQLを実行します。「&sql()」と同じ。
#Import
1つ以上のスキーマ名を指定する。
※全てののスキーマが、ネームスペースに存在している事
クエリが短くなるので、可読性が上がるかも?
ClassMethod testSample()
{
#Import developer_data
&sql(select count(*) INTO :cnt2 FROM Patient2)
w cnt2,!
}
複数指定した場合、同じテーブル名が複数存在するとエラーとなる。
→指定した全スキーマを検索するため
ClassMethod testSample2()
{
#Import developer_data, developer_copy
&sql(
select ID,カナ氏名,性別,漢字氏名,生年月日
into :ID, :kana, :sex, :name, :birthday
from Patient2
where patientId = '11730000001'
)
w %msg,!
}
【実行結果】
スキーマ内でテーブル ‘PATIENT2’ があいまいです: DEVELOPER_MACRO,DEVELOPER_COPY, DEVELOPER_DATA compiling embedded cached query from developer.macro.Sample.CLS
#sqlcompile (audit, mode, path, select)
先ずはサンプル
ClassMethod testSample()
{
#SQLCompile Audit=ON
#SQLCompile Mode=Embedded
#SQLCompile Path=developer_data
#SQLCompile Select=ODBC
&sql(
select ID,カナ氏名,性別,漢字氏名,生年月日
into :ID, :kana, :sex, :name, :birthday
from Patient2
where patientId = '11730000001'
)
w birthday,!
}
#sqlcompile path
#Importとほぼ同じ動作。
ただし、複数指定した場合、左から検索していき最初にHITしたスキーマを適用するので、「#Import」と異なり、複数テーブルが検索されても問題ない。
#sqlcompile select
データ形式モードを指定する。
値 | 説明 |
---|---|
Display | 表示モード |
Logical | 論理モード |
ODBC | ODBCモード |
Runtime | $SYSTEM.SQL.Util.SetOptionを使用して、値の変換が行える。 |
Text | Displayと同じ。表示モード |
FDBMS | 埋め込み SQLが FDBMS と同じデータをフォーマットできるようにする |
Runtimeのサンプル
ClassMethod testSample3()
{
#Import developer_data
#SQLCompile select=Runtime
s ptnId = "11730000001"
&sql(select 生年月日 into :birthday from Patient2 where patientId=:ptnId)
w birthday,!
d $SYSTEM.SQL.Util.SetOption("SelectMode",1) // 1:ODBC
&sql(select 生年月日 into :birthday from Patient2 where patientId=:ptnId)
w birthday
}
#sqlcompile mode
非推奨の為割愛しまーす。
#sqlcompile audit
後続の埋め込み SQL 文を監査データに登録するか、制御を行う。
スタジオで記述すると赤い波線が出ちゃいますが、動作に問題はありません。

コンパイル時に謎のエラーが出るときがありますが・・・問題無いようです。

さて、気を取り直して。
この機能「audit=ON」は、これだでは終わりません。
監査「%System/%SQL/EmbeddedStatement」をONにする
管理ポータルを起動し、[システム管理] > [セキュリティ] > [監査] > [システムイベントを構成]をクリックし「システム監査イベント」画面を表示します。
項目「%System/%SQL/EmbeddedStatement」の「状態変更」をクリックし、「Enabled=はい」に変更します。

監査データベースの閲覧
※先ずは、先ほどのSQLをターミナル等から実行します。
管理ポータルの[システム管理] > [セキュリティ] > [監査] > [監査データベースの閲覧]をクリックし「監査データベースの閲覧」画面を表示します。

先ほど実行したSQLが、監査レコードとして登録されています。
右端の「詳細」をクリックすると、SQLの実行結果が登録されます。

システムに対し、クリティカルなSQLに関しては、念のため監査データを作成しておくことで、安全対策を行うことができます。