第九章 疯狂Caché 宏和宏预处理器(二)

第九章 疯狂Caché 宏和宏预处理器(二)

#EndIf

#EndIf预处理器指令结束一组预处理器条件。它可以跟在#IfDef#IfUnDef#If#ElseI#Else之后。它的形式是:。

  // 指定条件开始的#IfDef、#If或#Else
  // 指定操作的后续缩进行
#EndIf

#EndIf指令关键字应该单独出现在一行中。同一行中#EndIf后面的任何内容都被视为注释,不会进行解析。

#Execute

#EXECUTE预处理器指令在编译时执行一行ObjectScript。它的形式是:

#Execute <ObjectScript code>

其中,#EXECUTE后面的内容是有效的ObjectScript代码。此代码可以引用编译时具有值的任何变量或属性;它还可以调用编译时可用的任何方法或例程。ObjectScript命令和函数始终可供调用。

#EXECUTE不返回任何指示代码是否成功运行的值。应用程序代码负责检查状态代码或其他此类信息;这可以使用附加的#EXECUTE指令或其他代码。

注意:如果将#EXECUTE与局部变量一起使用,可能会出现意想不到的结果。原因包括:

  • 编译时使用的变量在运行时可能超出范围。
  • 对于多个例程或方法,变量在引用时可能不可用。应用程序员不控制编译顺序的事实可能会加剧此问题。

例如,可以在编译时确定星期几并使用以下代码保存它:

/// d ##class(PHA.TEST.ObjectScript).TestMExecute()
ClassMethod TestMExecute()
{
	#Execute KILL ^DayOfWeek
	#Execute SET ^DayOfWeek = $ZDate($H,12)

	WRITE "Today is ",^DayOfWeek,".",!
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestMExecute()
Today is Wednesday.

其中,每次进行编译时都会更新^DayOfWeek全局设置。

#If

#if预处理器指令开始一个条件文本块。它将ObjectScript表达式作为参数,测试参数的真值,如果其参数的真值为真,则编译代码块。该代码块以#Else#ElseIf#EndIf指令结束。

#If <expression>
  // subsequent indented lines for specified actions

  // next preprocessor directive

其中<expression>是有效的ObjectScript表达式。如果<expression>的计算结果为非零值,则为true。

/// d ##class(PHA.TEST.ObjectScript).TestMIF()
ClassMethod TestMIF()
{
	KILL ^MyColor, ^MyNumber
	#Define ColorDay $ZDate($H,12)
	#If $$$ColorDay="Monday"
	SET ^MyColor = "Red"
	SET ^MyNumber = 1
	#ElseIf $$$ColorDay="Tuesday"
	SET ^MyColor = "Orange"
	SET ^MyNumber = 2
	#ElseIf $$$ColorDay="Wednesday"
	SET ^MyColor = "Yellow"
	SET ^MyNumber = 3
	#ElseIf $$$ColorDay="Thursday"
	SET ^MyColor = "Green"
	SET ^MyNumber = 4
	#ElseIf $$$ColorDay="Friday"
	SET ^MyColor = "Blue"
	SET ^MyNumber = 5
	#Else
	SET ^MyColor = "Purple"
	SET ^MyNumber = -1
	#EndIf
	WRITE ^MyColor, ", ", ^MyNumber
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestMIF()
Yellow, 3

此代码将ColorDay宏的值设置为编译时的日期名称。以#If 开头的条件语句使用ColorDay的值来确定如何设置^MyColor变量的值。这段代码有多个条件可以应用于ColorDay-周一之后的每个工作日都有一个条件;代码使用#ElseIf指令检查这些条件。失败的情况是#Else指令后面的代码。#EndIf关闭条件。

任意数量的空格可以分隔#if<expression>。但是,<expression>内不允许有空格。同一行<expression>后面的任何内容都被视为注释,不会进行解析。

注意:如果#if出现在方法代码中,并且参数不是文字值0或1,编译器将在子类中生成代码(而不是调用父类中的方法)。若要避免生成此代码,请测试值为0或1的条件,这会使代码更简单并优化性能。

#IfDef

#IfDef预处理器指令标记条件代码块的开始,其中执行取决于已定义的宏。它的形式是:

#IfDef macro-name

其中宏名称不带任何前导“$$$”字符。同一行上宏名后面的任何内容都被视为注释,不会进行解析。

代码的执行取决于已定义的宏。继续执行,直到到达#Else指令或结束#EndIf指令。

#IfDef仅检查是否定义了宏,而不检查其值是什么。因此,如果宏存在并且值为0(零),#IfDef仍然执行条件代码(因为宏确实存在)。

此外,由于#IfDef只检查宏的存在,因此只有一种替代情况(如果没有定义宏),由#Else指令处理。#ElseIf指令不能与#IfDef一起使用。

例如,下面根据宏的存在提供了一个简单的二进制开关:

/// d ##class(PHA.TEST.ObjectScript).Testifdef()
ClassMethod Testifdef()
{
	#Define Heads

	#IfDef Heads
		WRITE "定义了宏",!
	#Else
		WRITE "未定义宏",!
	#EndIf
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).Testifdef()
定义了宏

#IfNDef

#IfNDef预处理器指令标记条件代码块的开始,其中执行依赖于未定义的宏。它的形式是:

#IfNDef macro-name

其中宏名称不带任何前导“$$$”字符。同一行上宏名后面的任何内容都被视为注释,不会进行解析。

代码的执行取决于尚未定义宏。继续执行,直到到达#Else指令或结束#EndIf指令。#ElseIf指令不能与#IfNDef一起使用。

注意:#IfNDef有一个备用名称#IfUnDef。这两个名字的行为是一样的。

例如,下面提供了一个基于未定义宏的简单二进制开关:

/// d ##class(PHA.TEST.ObjectScript).TestIfNDef()
ClassMethod TestIfNDef()
{
	#Define Multicolor 256

	#IfNDef Multicolor
		SET NumberOfColors = 2
	#Else
		SET NumberOfColors = $$$Multicolor
	#EndIf
	
	WRITE "There are ",NumberOfColors," colors in use.",!
}

DHC-APP>d ##class(PHA.TEST.ObjectScript).TestIfNDef()
There are 256 colors in use.
 

#Import

#Import预处理器指令指定任何后续嵌入式SQL DML语句的模式搜索路径。

#Import指定要搜索的一个或多个架构名称,以提供非限定表、视图或存储过程名称的架构名称。可以指定单个架构名称,也可以指定以逗号分隔的架构名称列表。在当前命名空间中搜索架构。下面的示例显示了这一点,该示例定位Employees.Person表:

#Import Customers,Employees,Sales
  &sql(SELECT Name,DOB INTO :n,:date FROM Person)
  WRITE "name: ",n," birthdate: ",date,!
  WRITE "SQLCODE=",SQLCODE

搜索#Import指令中指定的所有架构。Person表必须正好位于#Import中列出的一个架构中。因为#Import需要在模式搜索路径中进行匹配,所以不使用系统范围的默认模式。

动态SQL使用%SchemaPath属性提供架构搜索路径来解析非限定名称。

#Import#SQLCompile path都指定了一个或多个用于解析非限定表名的架构名称。这两个指令之间的一些区别如下:

  • #IMPORT检测不明确的表名。#Import搜索所有指定的架构,检测所有匹配项。#SQLCompile path按从左到右的顺序搜索指定的架构列表,直到找到第一个匹配项。因此,#Import可以检测到不明确的表名;#SQLCompile path不能。例如,#Import Customers,Employees,Sales必须在Customers、Employees和Sales模式中恰好找到一个Person的匹配项;如果它找到此表名的多个匹配项,则会出现SQLCODE-43错误:“表‘Person’在模式中不明确”。

  • #IMPORT不能采用系统范围的默认值。如果#Import在其列出的任何模式中都找不到Person表,则会出现SQLCODE-30错误。如果#SQLCompile path在其列出的任何模式中都找不到Person表,它将检查系统范围的默认模式。

  • #Import指令是累加性的。如果有多个#Import指令,则所有指令中的架构必须精确解析为一个匹配项。指定第二个#Import不会停用前一个#Import中指定的架构名称列表。指定#SQLCompile path指令会覆盖前面的#SQLCompile path指令中指定的路径;#SQLCompile path不会覆盖前面的#Import指令中指定的架构名称。

Caché 忽略#import指令中不存在的架构名称。Caché 忽略#IMPORT指令中的重复方案名称。

如果表名已经限定,则#Import指令不适用。例如:

  #Import Voters
  #Import Bloggers
  &sql(SELECT Name,DOB INTO :n,:date FROM Sample.Person)
  WRITE "name: ",n," birthdate: ",date,!
  WRITE "SQLCODE=",SQLCODE

在本例中,Caché在示例模式中搜索Person表.

  • #Import适用于SQL DML语句。它可用于解析SQL SELECT查询以及INSERT、UPDATE和DELETE操作的非限定表名和视图名。#Import还可以用于解析SQL CALL语句中的非限定过程名称。
  • #Import不适用于SQL DDL语句。它不能用于解析数据定义语句(如CREATE TABLE和其他CREATE、ALTER和DROP语句)中的非限定表、视图和过程名称。如果在创建、修改或删除该项的定义时为表、视图或存储过程指定了非限定名称,则Caché将忽略#Import值并使用系统范围的默认模式。

请比较#SQLCompile路径预处理器指令。

#Include

#include预处理器指令加载包含预处理器指令的指定文件名。它的形式是:

#Include <filename>

其中,filename是包含文件的名称,不包括.inc后缀。包含文件通常与调用它们的文件位于同一目录中。它们的名称区分大小写。

要列出系统提供的所有#include文件名,请发出以下命令:

  ZWRITE ^rINC("%occInclude",0)
DHC-APP>  ZWRITE ^rINC("%occInclude",0)
^rINC("%occInclude",0)="64191,43009"
^rINC("%occInclude",0,0)=30
^rINC("%occInclude",0,1)="#include %occOptions"
^rINC("%occInclude",0,2)="#include %occConstant"
^rINC("%occInclude",0,3)="#include %occKeyword"
^rINC("%occInclude",0,4)="#include %occProcedure"
^rINC("%occInclude",0,5)="#include %occLocation"
^rINC("%occInclude",0,6)="#include %occReference"
^rINC("%occInclude",0,7)="#include %occCompiler"
^rINC("%occInclude",0,8)="#include %occReference2"
^rINC("%occInclude",0,9)="#include %occReferenceStorage"
^rINC("%occInclude",0,10)="#include %occXXX"
^rINC("%occInclude",0,11)="#include %occObject"
^rINC("%occInclude",0,12)="#include %occOID"
^rINC("%occInclude",0,13)="#include %occFlag"
^rINC("%occInclude",0,14)="#include %occQualifier"
^rINC("%occInclude",0,15)="#include %occEnvironment"
^rINC("%occInclude",0,16)="#include %occMessages"
^rINC("%occInclude",0,17)="#include %occStatus"
^rINC("%occInclude",0,18)="#include %occDiagnostics"
^rINC("%occInclude",0,19)="#include %occName"
^rINC("%occInclude",0,20)="#include %occClassname"
^rINC("%occInclude",0,21)="#include %occFunctions"
^rINC("%occInclude",0,22)="#include %occVersion"
^rINC("%occInclude",0,23)="#include %occRoutine"
^rINC("%occInclude",0,24)="#include %occJavaMetaDictionary"
^rINC("%occInclude",0,25)="#include %occDepend"
^rINC("%occInclude",0,26)="#include %occFile"
^rINC("%occInclude",0,27)="#include %sySecurity"
^rINC("%occInclude",0,28)="#include %syAudit"
^rINC("%occInclude",0,29)="#include %xmlDOM"
^rINC("%occInclude",0,30)=" "
^rINC("%occInclude",0,"SIZE")=628

要列出其中一个#include文件的内容,请指定所需的include文件。例如:

  ZWRITE ^rINC("%occStatus",0)
DHC-APP>  ZWRITE ^rINC("PHA.MOB.TEST.Macros",0)
^rINC("PHA.MOB.TEST.Macros",0)="65462,81298.178795"
^rINC("PHA.MOB.TEST.Macros",0,0)=36
^rINC("PHA.MOB.TEST.Macros",0,1)="#Define SelfString ""自定义"",!,""宏!"""
^rINC("PHA.MOB.TEST.Macros",0,2)=""
^rINC("PHA.MOB.TEST.Macros",0,3)="#Define sysDate +$h"
^rINC("PHA.MOB.TEST.Macros",0,4)=""
^rINC("PHA.MOB.TEST.Macros",0,5)="#Define sysTime $p($h,"","",2)"
^rINC("PHA.MOB.TEST.Macros",0,6)=""
^rINC("PHA.MOB.TEST.Macros",0,7)="#define zdh(%Date) ##class(websys.Conversions).DateHtmlToLogical(%Date)"
^rINC("PHA.MOB.TEST.Macros",0,8)="#define zd(%Date) ##class(websys.Conversions).DateLogicalToHtml(%Date)"
^rINC("PHA.MOB.TEST.Macros",0,9)=""
^rINC("PHA.MOB.TEST.Macros",0,10)="#Define PHA ""PHA"""
^rINC("PHA.MOB.TEST.Macros",0,11)="#Define IP ""IP"""
^rINC("PHA.MOB.TEST.Macros",0,12)="#Define OP ""OP"""
^rINC("PHA.MOB.TEST.Macros",0,13)="#Define DEC ""DEC"""
^rINC("PHA.MOB.TEST.Macros",0,14)=""

要列出在生成int例程时预处理的#include文件,请使用^例程global。请注意,这些#include指令不必在ObjectScript代码中引用:

ZWRITE ^ROUTINE("myroutine",0,"INC")

注意:在存储过程代码中使用#include时,它的前面必须有冒号字符“”,例如:

CREATE PROCEDURE SPxx() Language OBJECTSCRIPT {
 :#Include %occConstant
     SET x=##Lit($$$NULLOREF)
} 
```java
当在类的开头包含文件时,该指令不包括井号。因此,对于单个文件,它是:
```java
Include MyMacros

对于多个文件,它是:

Include (MyMacros, YourMacros)

例如,假设有一个包含宏的OS.inc头文件:

 #Define Windows
 #Define UNIX
#Include OS
 
#IfDef Windows
  WRITE "The operating system is not case-sensitive.",!
#Else
  WRITE "The operating system is case-sensitive.",!
#EndIf

#NoShow

#noshow预处理器指令结束作为包含文件一部分的注释部分。它的形式是:

#NoShow

其中#noshow跟在#show指令之后。强烈建议每个#Show都有相应的#noshow,即使注释部分继续到文件末尾也是如此。有关示例,请参阅#Show的条目。

#Show

#Show 指令开始一个注释部分,该部分是包含文件的一部分。默认情况下,包含文件中的注释不会出现在调用代码中。因此,#Show-#noshow括号外的include文件注释不会出现在引用代码中。

#Show

强烈建议每个#Show都有相应的#noshow,即使注释部分继续到文件末尾也是如此。

在下面的示例中,文件OS.inc(来自#include示例)包含以下注释:

#Show
  // If compilation fails, check the file 
  // OS-errors.log for the statement "No valid OS."
#NoShow
  // Valid values for the operating system are 
  // Windows or UNIX (and are case-sensitive).

其中前两行注释(以“如果编译失败.”开头)。出现在包含包含文件和后两行注释的代码中(以“Valid Values.”开头)。仅出现在包含文件本身中。

#SQLCompile Audit

SQLCompile Audit预处理器指令是一个布尔值,它指定是否审核任何后续的嵌入式SQL语句。它的形式是:

#SQLCompile Audit=value

其中,Value处于启用或禁用状态。

要使此宏预处理器指令生效,必须启用%SYSTEM/%SQL/EmbeddedStatement系统审核事件。默认情况下,此系统审核事件未启用。

#SQLCompile Mode

#SQLCompile Mode预处理器指令指定任何后续嵌入式SQL语句的编译模式。它的形式是:

#SQLCompile Mode=value
  • Embedded - 在运行前编译ObjectScript代码和嵌入式SQL代码。这是默认设置。
  • Deferred - 编译ObjectScript代码,但将编译嵌入式SQL代码推迟到运行时。这能够编译包含引用编译时尚不存在的表的SQL的例程。

注意:不应将#SQLCompile Mode=DEFERED%SYSTEM.SQL.SetCompileModeDeferred()方法混为一谈。

延迟模式和嵌入式模式语句在其他方面是相同的。延迟模式语句和嵌入式模式语句都是静态的。也就是说,它们不能在运行时动态组装并提交以供处理。如果需要动态代码,请使用动态SQL。

延迟模式可用于INSERT、UPDATE和DELETE操作,以及返回单行数据的SELECT语句。延迟SQL不能用于声明游标和提取数据行的多行SELECT语句;尝试这样做会生成#5663编译错误。与嵌入式SQL一样,延迟SQL不检查权限。

在延迟模式下,SQL可以引用编译时尚不存在的表、用户定义函数和其他实体。

如果嵌入式SQL语句包含无效的SQL语句(例如,SQL语法错误),宏预处理器将生成代码\“**SQL语句无法编译**\”,并继续编译ObjectScript代码。因此,当使用包含无效嵌入式SQL的方法编译类时,会报告SQL错误,但会生成该方法。运行此方法时,无效的SQL会导致错误。

嵌入式SQL提供最佳的SQL性能,应尽可能使用。延迟SQL通常比动态SQL更有效。将Transact-SQL或Informix SPL存储过程转换为Caché时,可能需要延迟SQL。存储过程的Caché转换工具支持此功能。

#SQLCompile Path

#SQLCompile path预处理器指令指定任何后续嵌入式SQL DML语句的模式搜索路径。它的形式是:

#SQLCompile Path=schema1[,schema2[,...]]

其中schema是用于在当前名称空间中查找非限定SQL表名、视图名或过程名的模式名。可以指定一个架构名称或以逗号分隔的架构名称列表。将按指定的顺序搜索架构。搜索结束,并在出现第一个匹配时执行DML操作。如果没有模式包含匹配项,则搜索系统范围内的默认模式。

因为架构是按指定的顺序搜索的,所以不会检测到有歧义的表名。#Import预处理程序指令还从模式名列表中向非限定的SQL表、视图或过程名提供模式名;#Import会检测不明确的名称。

Caché忽略#SQLCompile path指令中不存在的架构名称。Caché忽略#SQLCompile path`指令中的重复模式名称。

  • #SQLCompile path应用于SQL DML语句。它可用于解析SQL SELECT查询以及INSERT、UPDATE和DELETE操作的非限定表名和视图名。#SQLCompile path还可用于解析SQL CALL语句中的非限定过程名称。
  • #SQLCompile path不适用于SQL DDL语句。它不能用于解析数据定义语句(如CREATE TABLE和其他CREATE、ALTER和DROP语句)中的非限定表、视图和过程名称。如果在创建、修改或删除该项的定义时为表、视图或存储过程指定了非限定名称,则Caché将忽略#SQLCompile path值并使用系统范围的默认模式。

动态SQL使用%SchemaPath属性提供架构搜索路径来解析非限定名称。

以下示例将非限定表名Person解析为Sample.Person表。它首先搜索Cinema模式(不包含名为Person的表),然后搜索示例模式:

#SQLCompile Path=Cinema,Sample
  &sql(SELECT Name,Age
       INTO :a,:b
       FROM Person)
  WRITE "Name is: ",a,!
  WRITE "Age is: ",b

除了将架构名称指定为搜索路径项外,还可以指定以下关键字:

  • CURRENT_PATH:指定在前面的#SQLCompile path预处理器指令中定义的当前模式搜索路径。
    这通常用于将架构添加到现有架构搜索路径的开头或结尾,如下例所示:
#SQLCompile Path=schema_A,schema_B,schema_C
#SQLCompile Path=CURRENT_PATH,schema_D
  • CURRENT_SCHEMA:指定当前模式容器类名。如果在类方法中定义了#SQLCompile path,则CURRENT_SCHEMA是映射到当前类包的模式。如果在.Mac例程中定义了#SQLCompile path,则CURRENT_SCHEMA为配置默认模式。

例如,如果在类User.MyClass中定义一个类方法,指定#SQLCompile path=CURRENT_SCHEMA,则默认情况下,CURRENT_SCHEMA将解析为SQLUser,因为SQLUser是用户包的默认架构名称。当在不同的包中有一个父类和子类,并且在父类中定义了一个具有未限定表名的SQL查询的方法时,这很有用。使用CURRENT_SCHEMA,可以将表名解析为父类中的父类模式和子类中的子类模式。如果没有设置CURRENT_SCHEMA搜索路径,表名将解析为两个类中的父类模式。

如果在触发器中使用#SQLCompile PATH=CURRENT_SCHEMA,则使用架构容器类名。例如,如果类pkg1.myclass具有指定#SQLCompile path=current_schema的触发器,并且类pkg2.myclass扩展了pkg1.myclass,则在编译pkg2.myclass时,caché将触发器中的SQL语句中的非限定表名解析为包pkg2的模式。

  • DEFAULT_SCHEMA指定系统范围的默认模式。此关键字使能够在搜索其他列出的架构之前,将系统范围内的默认架构作为架构搜索路径中的一个项目进行搜索。如果搜索路径中指定的所有架构都不匹配,则在搜索架构搜索路径后,始终搜索系统范围内的默认架构。

如果指定架构搜索路径,则SQL查询处理器在尝试解析非限定名称时首先使用架构搜索路径。如果它没有找到指定的表或过程,则会查找通过#Import(如果指定)提供的模式或配置的系统范围默认模式。如果在这些位置中没有找到指定表,则会生成SQLCODE-30错误。

#SQLCompile path可以与#SQLCompile mode值嵌入或延迟一起使用。

架构搜索路径的范围是在其中定义它的例程或方法。如果在类方法中指定了架构路径,则它仅适用于该类方法,而不适用于类中的其他方法。如果它是在.Mac例程中指定的,它将从例程中的该点向前应用,直到找到另一个#SQLCompile path指令,或者到达例程的末尾。

架构是为当前命名空间定义的。

猜你喜欢

转载自blog.csdn.net/yaoxin521123/article/details/106249477