方法
结构化类型的数据库方法是一组输入数据值与一组结果值之间的关系,其中第一个输入值 (或 主题参数) 具有相同类型,或者是该方法的主题类型 (也称为 主题参数) 的子类型。
例如,类型为 ADDRESS 的称为 CITY 的方法可以传递类型为 VARCHAR 的输入数据值,结果是 ADDRESS (或 ADDRESS 的子类型)。
方法是隐式或显式定义的,作为用户定义的结构化类型定义的一部分。
将为每种结构化类型创建隐式定义的方法。 观察器方法 是针对结构化类型的每个属性定义的。 观察器方法允许应用程序获取类型实例的属性值。 还为每个属性定义了 Mutator 方法 ,允许应用程序通过更改类型实例的属性值来更改类型实例。 先前描述的 CITY 方法是类型 ADDRESS 的 mutator 方法的示例。
显式定义的方法或 用户定义的方法是通过使用 CREATE TYPE (或 ALTER TYPE ADD METHOD) 和 CREATE METHOD 语句的组合向 SYSCAT.ROUTINES中的数据库注册的方法。 为结构化类型定义的所有方法都是在与该类型相同的模式中定义的。
针对结构化类型的用户定义方法通过添加可应用于数据库引擎中的结构化类型实例的方法定义 (由用户或第三方供应商提供) 来扩展数据库系统的功能。 定义数据库方法使数据库能够利用应用程序所使用的引擎中的相同方法,从而在应用程序与数据库之间提供更多协同作用。
外部和 SQL 用户定义的方法
用户定义的方法可以是外部方法,也可以是基于 SQL 表达式的方法。 对数据库定义了外部方法,该方法引用了对象代码库以及该库中将在调用该方法时执行的函数。 基于 SQL 表达式的方法在调用该方法时返回 SQL 表达式的结果。 此类方法不需要任何对象代码库,因为它们完全以 SQL 编写。
用户定义的方法可以在每次调用时返回单值答案。 此值可以是结构化类型。 可以将方法定义为 类型保留 (使用 SELF AS RESULT) ,以允许将主题自变量的动态类型作为方法的返回类型返回。 所有隐式定义的增变器方法都是类型保留。
方法特征符
方法由其主题类型,方法名称,参数数量及其参数的数据类型标识。 这称为 方法特征符,它在数据库中必须唯一。
- 参数的数目或参数的数据类型不同,或者
- 这些方法是同一方法层次结构的一部分 (即,这些方法处于覆盖关系或覆盖同一原始方法) ,或者
- 不存在相同的函数特征符 (使用主题类型或其任何子类型或超类型作为第一个参数)。
具有多个方法实例的方法名称称为 重载方法。 可以在类型中重载方法名称,在这种情况下,该类型有多个方法使用该名称 (所有这些方法都具有不同的参数类型)。 在主题类型层次结构中也可以重载方法名称,在这种情况下,在类型层次结构中存在多个使用该名称的方法。 这些方法必须具有不同的参数类型。
可以通过 (在允许的上下文中) 引用方法名称来调用方法,该方法前面是对结构化类型实例 (主体参数) 的引用以及双点运算符。 括在括号内的自变量列表必须跟在后面。 实际调用哪种方法取决于主题类型的静态类型,使用以下部分中描述的方法解析过程。 还可以使用函数调用来调用定义为 WITH FUNCTION ACCESS 的方法,在这种情况下,函数解析的常规规则适用。
如果函数解析导致定义了 WITH FUNCTION ACCESS 的方法,那么将处理方法调用的所有后续步骤。
通过 EXECUTE 特权控制对方法的访问。 GRANT 和 REVOKE 语句用于指定可以或不能执行特定方法或一组方法的人员。 需要 EXECUTE 特权 (或 DATAACCESS 权限) 来调用方法。 方法的定义者自动接收 EXECUTE 特权。 在所有底层对象上具有 WITH GRANT 选项的外部方法或 SQL 方法的定义者也会接收对该方法具有 EXECUTE 特权的 WITH GRANT 选项。 然后,定义程序 (或具有 ACCESSCTRL 或 SECADM 权限的授权标识) 必须将其授予要从任何 SQL 语句调用该方法的用户,或者在任何 DDL 语句 (例如 CREATE VIEW , CREATE TRIGGER 或在定义约束时) 中引用该方法的用户。 如果未向用户授予 EXECUTE 特权,那么方法解析算法将不考虑该方法,即使它是更好的匹配项。
方法解析
在方法调用之后,数据库管理器必须确定具有相同名称的可能方法中的哪些是 最适合
的方法。 在方法解析期间不考虑函数 (内置函数或用户定义函数)。
自变量 是调用时传递到方法的值。 在 SQL 中调用方法时,将传递主体参数 (某种结构化类型) 以及零个或多个参数的列表。 它们是位置性的,因为自变量的语义由其在自变量列表中的位置确定。 参数 是方法输入的正式定义。 当向数据库隐式定义方法 (系统为类型生成) 或由用户 (用户定义的方法) 定义方法时,将指定其参数 (以主题参数作为第一个参数) ,并且它们的定义顺序定义它们的位置及其语义。 因此,每个参数都是方法的特定位置输入。 在调用时,自变量根据其在自变量列表中的位置与特定参数相对应。
数据库管理器使用在调用中给定的方法的名称,对方法的 EXECUTE 特权,自变量的数目和数据类型,对主题自变量的静态类型 (以及它的超类型) 具有相同名称的所有方法以及它们相应参数的数据类型作为决定是否选择方法的基础。 以下是决策过程的可能结果:
- 一种特定的方法被认为是最适合的。 例如,给定类型 SITE 的名为 RISK 的方法,其特征符定义为:
以下方法调用 (其中 ST 是 SITE 列, DB 是 DOUBLE 列):PROXIMITY(INTEGER) FOR SITE PROXIMITY(DOUBLE) FOR SITE
然后,将选择第二个靠近。SELECT ST..PROXIMITY(DB) ...以下方法调用 (其中 SI 是 SMALLINT 列):
将选择第一个近似值,因为 SMALLINT 可以提升为 INTEGER ,并且比 DOUBLE 匹配得更好,它在优先顺序列表下更远。SELECT ST..PROXIMITY(SI) ...在考虑作为结构化类型的自变量时,优先顺序列表包含该自变量的静态类型的超类型。 最佳拟合是使用在结构化类型层次结构中与函数自变量的静态类型最接近的超类型参数定义的函数。
- 没有任何方法被视为可接受的拟合。 例如,给定前一种情况中相同的两个函数以及以下函数引用 (其中 C 是 CHAR (5) 列):
参数与两个邻近函数的参数不一致。SELECT ST..PROXIMITY(C) ... - 根据类型层次结构中的方法以及调用时传递的参数的数目和数据类型来选择特定方法。 例如,为类型 ITE 和 DRILLSITE (SITE 的子类型) 指定了名为 RISK 的方法,其特征符定义为:
以及以下方法调用 (其中 DRST 是 DRILLSITE 列, DB 是 DOUBLE 列):RISK(INTEGER) FOR DRILLSITE RISK(DOUBLE) FOR SITE
将选择第二个 RISK ,因为可以将 DRILLSITE 提升到 SITE。SELECT DRST..RISK(DB) ...以下方法引用 (其中 SI 是 SMALLINT 列):
将选择第一个 RISK ,因为可以将 SMALLINT 提升为 INTEGER (在优先顺序列表上比 DOUBLE 更接近) ,并且 DRILLSITE 比 SITE (超类型) 更匹配。SELECT DRST..RISK(SI) ...同一类型层次结构中的方法不能具有相同的特征符,请考虑主体参数以外的参数。
确定最佳拟合度
将自变量的数据类型与所考虑方法的参数的已定义数据类型进行比较,将构成一组相似命名方法中的哪个方法是 最佳拟合
的决策基础。 请注意,正在考虑的方法的结果的数据类型不会进入此确定。
对于方法解析,在确定最佳拟合时,是否可以将输入参数的数据类型提升到相应参数的数据类型。 与函数解析不同,在确定最佳拟合时,不会考虑输入自变量是否可以隐式强制转换为相应参数的数据类型。 在方法解析期间不考虑模块,因为无法在模块中定义方法。
使用以下步骤执行方法解析:
- 首先,从目录 (SYSCAT.ROUTINES) 使以下所有内容都成立:
- 方法名称与调用名称匹配,并且主题参数是同一类型或主题参数的静态类型的超类型。
- 调用者对该方法具有 EXECUTE 特权。
- 定义的参数数目与调用相匹配。
- 每个调用自变量都与数据类型中方法的相应定义参数相匹配,或者对其
可提升
。
- 接下来,从左到右考虑方法调用的每个自变量。 最左边的自变量 (因此是第一个自变量) 是隐式 SELF 参数。 例如,为类型 ADDRESS_T 定义的方法具有类型 ADDRESS_T 的隐式第一个参数。 对于每个自变量,请消除并非与该自变量最佳匹配的所有函数。 给定自变量的最佳匹配是出现在优先顺序列表中的第一个数据类型,该数据类型对应于存在具有该数据类型的参数的函数的自变量数据类型。 在此比较中不考虑长度,精度,小数位和 FOR BIT DATA 属性。 For example, a DECIMAL(9,1) argument is considered an exact match for a DECIMAL(6,5) parameter, DECFLOAT (34) 参数被视为与 DECFLOAT (16) 参数完全匹配, and a VARCHAR(19) argument is an exact match for a VARCHAR(6) parameter.
用户定义的结构化类型自变量的最佳匹配是自身; 下一个最佳匹配是自变量的直接超类型,依此类推。 请注意,仅考虑结构化类型自变量的静态类型 (声明的类型) ,而不考虑动态类型 (最具体的类型)。
- 在步骤 2 之后最多保留一个候选方法。 这是选择的方法。
- 如果在步骤 2 之后没有任何候选方法,那么将返回错误 (SQLSTATE 42884)。
方法解析示例
以下是成功方法解析的示例。
CREATE METHOD FOO (CHAR(5), INT, DOUBLE) FOR HEADOFSTATE SPECIFIC FOO_1 ...
CREATE METHOD FOO (INT, INT, DOUBLE) FOR HEADOFSTATE SPECIFIC FOO_2 ...
CREATE METHOD FOO (INT, INT, DOUBLE, INT) FOR HEADOFSTATE SPECIFIC FOO_3 ...
CREATE METHOD FOO (INT, DOUBLE, DOUBLE) FOR EMPEROR SPECIFIC FOO_4 ...
CREATE METHOD FOO (INT, INT, DOUBLE) FOR EMPEROR SPECIFIC FOO_5 ...
CREATE METHOD FOO (SMALLINT, INT, DOUBLE) FOR EMPEROR SPECIFIC FOO_6 ...
CREATE METHOD FOO (INT, INT, DEC(7,2)) FOR GOVERNOR SPECIFIC FOO_7 ... SELECT E..FOO(I1, I2, D) ...通过算法进行跟踪 ...- FOO_7 作为候选人被淘汰,因为类型总督是皇帝的子类型 (而不是超类型)。
- FOO_3 作为候选项被消除,因为它有错误数量的参数。
- 将消除 FOO_1 和 FOO_6 ,因为在这两种情况下,无法将第一个自变量 (不是主题自变量) 提升到第一个参数的数据类型。 由于剩余多个候选项,因此将按顺序考虑这些自变量。
- 对于主题自变量, FOO_2 是超类型,而 FOO_4 和 FOO_5 与主题自变量匹配。
- 对于第一个自变量,其余方法 FOO_4 和 FOO_5与自变量类型完全匹配。 不能从考虑中消除任何方法; 因此必须检查下一个自变量。
- 对于第二个自变量, FOO_5 是完全匹配的,但 FOO_4 不是完全匹配的,因此会将其排除在考虑之外。 这将保留 FOO_5 作为选择的方法。
方法调用
一旦选择了该方法,仍可能存在不允许使用该方法的原因。
STEP(SMALLINT) FOR TYPEA RETURNS CHAR(5)
STEP(DOUBLE) FOR TYPEA RETURNS INTEGER和以下方法参考 (其中 S 是 SMALLINT 列, TA 是 TYPEA 列): SELECT 3 + TA..STEP(S) ...然后,因为在参数类型上存在完全匹配,所以选择第一个 STEP。 在语句上发生错误,因为结果类型是 CHAR (5) ,而不是数字类型,这对于加法运算符的自变量是必需的。从已选择的方法开始,将使用 动态分派方法
中描述的算法在编译时构建可分派方法集。 动态分派方法
中描述了调用的确切方法。
- 函数解析后的静态结果类型与方法调用的主题自变量的静态类型相同
- 调用方法时的动态结果类型与方法调用的主体参数的动态类型相同。
在方法调用的自变量与所选方法的参数的数据类型不完全匹配的情况下,将使用与列分配相同的规则在执行时将自变量转换为参数的数据类型。 这包括参数与参数之间的精度,刻度或长度不同的情况,但不包括参数的动态类型是参数的静态类型的子类型的情况。
方法的动态调度
方法提供功能并封装类型的数据。 方法是为类型定义的,并且可以始终与此类型相关联。 该方法的其中一个参数是隐式 SELF 参数。 SELF 参数是已为其声明方法的类型。 在 DML 语句中调用方法时作为 SELF 自变量传递的自变量称为 subject。
使用方法解析 (请参阅 方法解析) 选择方法时,或者在 DDL 语句中指定了方法时,此方法称为 最具体的适用授权方法
。 如果主体为结构化类型,那么该方法可能具有一个或多个覆盖方法。 然后,根据运行时主题的动态类型 (最具体类型) 确定要调用的方法。 此确定称为 确定最具体的可分派方法
。 此处描述了该过程。
- 在方法层次结构中查找最具体的适用授权方法所包含的原始方法。 这称为 根方法。
- 创建可分派方法集,包括以下内容:
- 最具体的适用授权方法。
- 任何覆盖最特定适用的授权方法的方法,以及为此调用的主题的子类型定义的方法。
- 确定最具体的可分派方法,如下所示:
- 从任意方法开始,该方法是可分派方法集合的元素,并且是主体的动态类型或其超类型之一的方法。 这是初始最具体的可分派方法。
- 迭代可分派方法集的元素。 对于每个方法: 如果为定义了最特定可分派方法的类型的其中一个正确子类型定义了该方法,并且为主题的最特定类型的其中一个超类型定义了该方法,那么重复步骤 2 ,并将此方法作为最特定可分派方法; 否则,继续迭代。
- 调用最具体的可分派方法。
示例:
给出了三种类型, "人员" , "员工" 和 "经理"。 有一种原始方法 "收入" ,为 "人员" 定义,用于计算一个人的收入。 一个人在默认情况下是失业的 (一个孩子,一个退休人员等等)。 因此,类型 "Person" 的 "收入" 始终返回零。 对于类型 "Employee" 和类型 "Manager" ,必须应用不同的算法来计算收入。 因此, "Person" 类型的方法 "收入" 在 "Employee" 和 "Manager" 中被覆盖。
CREATE TABLE aTable (id integer, personColumn Person);
INSERT INTO aTable VALUES (0, Person()), (1, Employee()), (2, Manager());列出所有最低收入为 40000 元的人: SELECT id, person, name
FROM aTable
WHERE person..income() >= 40000;使用方法解析选择 "Person" 类型的方法 "income" 作为最具体的适用授权方法。
- 根方法是 "个人" 本身的 "收入"。
- 上一个算法的第二步是进行构造可分派方法的集合:
- 包含 "Person" 类型的方法 "收入" ,因为它是最具体的适用授权方法。
- 将包括类型为 "Employee" 的方法 "income" 和类型为 "Manager" 的方法 "income" ,因为这两种方法都覆盖根方法,并且 "Employee" 和 "Manager" 都是 "Person" 的子类型。
因此,可分派方法集为: {"income" for "Person", "income" for "Employee", "income" for "Manager"}。
- 确定最具体的可分派方法:
- 对于其最特定类型为 "Person" 的主题:
- 让初始最具体的可分派方法为 "人员" 类型的 "收入"。
- 因为在为适当的子类型 "Person" 定义的可分派方法集合中没有其他方法,而对于主题的最特定类型的超类型, "Person" 类型的 "收入" 是最具体的可分派方法。
- 对于最特定类型为 "Employee" 的主题:
- 让初始最具体的可分派方法为 "人员" 类型的 "收入"。
- 迭代可分派方法集。 因为类型 "Employee" 的方法 "income" 是针对 "Person" 的适当子类型定义的,并且针对主题的最特定类型的超类型 (注: 类型是自己的超类型和子类型。) , "Employee" 类型的方法 "income" 与最具体的可分派方法更匹配。 重复此步骤,将类型为 "Employee" 的方法 "income" 作为最具体的可分派方法。
- 因为在为 "Employee" 的适当子类型定义的可分派方法集中没有其他方法,而对于主题的最具体类型的超类型, "Employee" 类型的方法 "收入" 是最具体的可分派方法。
- 对于最特定类型为 "Manager" 的主题:
- 让初始最具体的可分派方法为 "人员" 类型的 "收入"。
- 迭代可分派方法集。 因为类型 "Manager" 的方法 "income" 是针对 "Person" 的适当子类型以及针对主题的最特定类型的超类型定义的 (注: 类型是自己的超类型和子类型。) , "Manager" 类型的方法 "income" 与最具体的可分派方法更匹配。 将类型为 "Manager" 的方法 "收入" 作为最具体的可分派方法重复此步骤。
- 因为在为适当的子类型 "Manager" 定义的可分派方法集中没有其他方法,而对于主题的最特定类型的超类型,类型 "Manager" 的方法 "income" 是最具体的可分派方法。
- 对于其最特定类型为 "Person" 的主题:
- 调用最具体的可分派方法。