这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

用户定义的扩展

用户定义的扩展 (UDx) 是扩展 Vertica 功能的组件,例如,新类型的数据分析以及解析和加载新类型数据的能力。

本节概述如何安装和使用 UDx。如果您使用的是第三方开发的 UDx,请查阅其文档以获取详细的安装和使用说明。

1 - 加载 UDx

库中包含用户定义扩展 (UDx)。库可包含多个 UDx。要将 UDx 添加到 Vertica,您必须执行下列操作:

  1. 部署库(每个库一次)。

  2. 创建每个 UDx(每个 UDx 一次)。

如果您使用的是用 Java 编写的 UDx,则还必须设置 Java 运行时环境。请参阅在 Vertica 主机上安装 Java

部署库

要将库部署到 Vertica 数据库,请执行下列操作:

  1. 将包含您的函数的 UDx 共享库文件 (.so)、Python 文件、Java JAR 文件或 R 函数文件复制到 Vertica 群集上的节点。您不需要将其复制到每个节点。

  2. 连接到从中复制库的节点(例如使用 vsql)。

  3. 使用 CREATE LIBRARY 语句将库添加到数据库编录。

    => CREATE LIBRARY libname AS '/path_to_lib/filename'
       LANGUAGE 'language';
    

    libname 是要用来引用库的名称,path_to_lib/filename 是复制到主机的库或 JAR 文件的完全限定路径,language 是实施语言。

    例如,如果创建一个名为 TokenizeStringLib.jar 的 JAR 文件,然后将其复制到数据库管理员帐户的主目录,将使用此命令加载库:

    => CREATE LIBRARY tokenizelib AS '/home/dbadmin/TokenizeStringLib.jar'
       LANGUAGE 'Java';
    

可以将任意数量的库加载到 Vertica。

权限

超级用户可以创建、修改和删除任何库。具有 UDXDEVELOPER 角色或显式授权的用户也可以对库进行操作,如下表所示:

创建 UDx 函数

加载库之后,您可以使用 SQL 语句(例如 CREATE FUNCTIONCREATE SOURCE)定义各个 UDx。这些语句将 SQL 函数名分配给库中的扩展类。它们会将 UDx 添加到数据库编录,并在数据库重新启动后保持可用。

所使用的语句取决于要声明的 UDx 的类型,如下表所示:

如果给定名称的 UDx 已存在,您可以替换它或指示 Vertica 不替换它。要替换它,请使用 OR REPLACE 语法,如下例所示:


=> CREATE OR REPLACE TRANSFORM FUNCTION tokenize
   AS LANGUAGE 'C++' NAME 'TokenFactory' LIBRARY TransformFunctions;
CREATE TRANSFORM FUNCTION

您可能希望替换现有函数以在隔离和非隔离模式之间进行切换。

或者,如果该函数已经存在,您可以使用 IF NOT EXISTS 来防止再次创建该函数。您可能希望在需要并因此加载 UDx 的升级或测试脚本中使用它。通过使用 IF NOT EXISTS,您可以保留包括隔离状态在内的原始定义。以下示例显示了此语法:

--- original creation:
=> CREATE TRANSFORM FUNCTION tokenize
   AS LANGUAGE 'C++' NAME 'TokenFactory' LIBRARY TransformFunctions NOT FENCED;
CREATE TRANSFORM FUNCTION

--- function is not replaced (and is still unfenced):
=> CREATE TRANSFORM FUNCTION IF NOT EXISTS tokenize
   AS LANGUAGE 'C++' NAME 'TokenFactory' LIBRARY TransformFunctions FENCED;
CREATE TRANSFORM FUNCTION

将 UDx 添加到数据库之后,您便可以在 SQL 语句中使用扩展。数据库超级用户能够向用户授予 UDx 访问权限。有关详细信息,请参阅GRANT(用户定义的扩展)

只要您调用 UDx,Vertica 就会在群集中的每个节点上创建 UDx 类的一个实例,并将需要处理的数据提供给该实例。

2 - 在 Vertica 主机上安装 Java

如果您使用的是用 Java 编写的 UDx,请按照本节中的说明进行操作。

必须在群集中的每个主机上安装 Java 虚拟机 (Java Virtual Machine, JVM),以使 Vertica 能够执行 Java UDx。

在 Vertica 群集上安装 Java 分为两步:

  1. 在群集中的所有主机上安装 Java 运行时。

  2. 设置 JavaBinaryForUDx 配置参数,让 Vertica 了解 Java 可执行文件的位置。

对于基于 Java 的功能,Vertica 要求使用 64 位 Java 6(Java 版本 1.6)或更高版本 Java 运行时。Vertica 支持 Oracle 或 OpenJDK 中的运行时。您可以选择安装 Java 运行时环境 (JRE) 或者 Java 开发工具包 (JDK),因为 JDK 还包括 JRE。

许多 Linux 分发版都含有一个 OpenJDK 运行时包。有关安装和配置 OpenJDK 的信息,请参阅您的 Linux 分发版的文档。

要安装 Oracle Java 运行时,请参阅 Java Standard Edition (SE) 下载页。您通常需要以 root 身份运行安装包才能安装它。有关说明,请参阅下载页。

如果已经在每台主机上安装了 JVM,请确保 java 命令位于搜索路径中,而且通过运行以下命令来调用正确的 JVM:

$ java -version

此命令的输出类似如下内容:

java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

设置 JavaBinaryForUDx 配置参数

JavaBinaryForUDx 配置参数可以让 Vertica 了解到执行 Java UDx 的 JRE 的位置。在群集中的所有节点上安装 JRE 后,请将此参数设置为 Java 可执行文件的绝对路径。您可以使用 Java 安装程序创建的符号链接(例如 /usr/bin/java)。如果 Java 可执行文件位于 shell 搜索路径中,您可以从 Linux 命令行 shell 中运行以下命令来获得 Java 可执行文件的路径。

$ which java
/usr/bin/java

如果 java 命令不在 shell 搜索路径中,请使用 JRE 安装目录中的 Java 可执行文件路径。假设您已在 /usr/java/default 中安装了 JRE(Oracle 提供的安装包会在此安装 Java 1.6 JRE)。在此情况下,Java 可执行文件为 /usr/java/default/bin/java

要设置配置参数,您可以 数据库超级用户身份执行以下语句:

=> ALTER DATABASE DEFAULT SET PARAMETER JavaBinaryForUDx = '/usr/bin/java';

有关设置配置参数的详细信息,请参阅 ALTER DATABASE

要查看配置参数的当前设置,请查询 CONFIGURATION_PARAMETERS 系统表:

=> \x
Expanded display is on.
=> SELECT * FROM CONFIGURATION_PARAMETERS WHERE parameter_name = 'JavaBinaryForUDx';
-[ RECORD 1 ]-----------------+----------------------------------------------------------
node_name                     | ALL
parameter_name                | JavaBinaryForUDx
current_value                 | /usr/bin/java
default_value                 |
change_under_support_guidance | f
change_requires_restart       | f
description                   | Path to the java binary for executing UDx written in Java

设置配置参数后,Vertica 可以在群集中的每个节点上查找 Java 可执行文件。

3 - UDx 限制

您不能对包含复杂类型的输入使用任何 UDx(请参阅复杂类型)。例如,您不能转换或聚合 ROW 列。UDSF(不是其他 UDx)可以返回 ROW。

部分 UDx 类型具有特殊注意事项或限制。

聚合函数

您不能在具有多个聚合函数的查询中使用 DISTINCT 子句。

分析函数

UDAnF 不支持使用 ROWS 搭建窗体框架

使用 Vertica 的内置分析函数时,UDAnF 无法与 MATCH 子句函数 一起使用。

标量函数

如果应用 UDSF 的结果是无效记录,即使 CopyFaultTolerantExpressions 设置为 true,COPY 也会中止加载。

从 UDSF 返回的 ROW 不能用作 COUNT 的实参。

转换函数

包括 UDTF 的查询不能:

加载函数

安装不受信任的 UDL 函数可能会影响服务器安全。UDxs 可以包含任意代码。尤其是用户定义的源函数可以从任意位置读取数据。恰当的安全限制要由函数的开发人员来实施。超级用户不得向不受信任用户授予对 UDx 的访问权限。

不能修改 UDL 函数。

4 - 隔离和非隔离模式

用 C++ 编程语言编写的用户定义扩展 (UDx) 可以选择在隔离或非隔离模式下运行。隔离模式在一个单独的 zygote 进程中运行主 Vertica 进程之外的 UDx 代码。使用非隔离模式的 UDx 直接在 Vertica 进程中运行。

隔离模式

您可以在隔离模式下运行大多数 C++ UDx。隔离模式使用单独的 zygote 进程,因此隔离的 UDx 崩溃不会影响核心 Vertica 进程。在隔离模式下运行 UDx 代码时,将造成轻微影响。平均来说,使用隔离模式会使执行时间比非隔离模式增加大约 10%。

目前,所有 C++ UDx(用户定义的聚合除外)都可以使用隔离模式。使用 Python、R 和 Java 编程语言开发的所有 UDx 都必须在隔离模式下运行,因为 Python、R 和 Java 运行时无法直接在 Vertica 进程中运行。

使用隔离模式不会影响 UDx 的开发。对于支持隔离模式的 UDx,默认启用隔离模式。或者,您可以发出带有 NOT FENCED 修饰符的 CREATE FUNCTION 命令来禁用该函数的隔离模式。此外,您可以使用 ALTER FUNCTION 命令在任何支持隔离模式的 C++ UDx 上启用或禁用隔离模式。

非隔离模式

非隔离 UDx 在 Vertica 中运行,因此它们的开销很小,并且执行速度几乎与 Vertica 自有的内置函数一样快。但是,由于它们直接在 Vertica 中运行,因此其代码中的任何缺陷(例如内存泄漏)会导致 Vertica 主进程不稳定,从而关闭一个或多个数据库节点。

关于 zygote 进程

Vertica zygote 进程随 Vertica 一起启动。每个节点具有单个 zygote 进程。从属进程是“按需”创建的。zygote 会侦听请求,并在用户调用 UDx 时生成 UDx 端会话(在隔离模式下运行 UDx)。

关于隔离模式日志记录:

在隔离模式下运行的 UDx 代码记录在 UDxZygote.log 中,并存储在 Vertica 编录目录中的 UDxLogs 目录中。从属进程的日志条目用 UDx 语言(例如 C++)、节点以及 zygote 进程 ID 和 UDxSideProcess ID 表示。

例如,对于以下查询,将返回当前隔离的进程:

=> SELECT * FROM UDX_FENCED_PROCESSES;
    node_name     |   process_type   |            session_id            |  pid  | port  | status
------------------+------------------+----------------------------------+-------+-------+--------
 v_vmart_node0001 | UDxZygoteProcess |                                  | 27468 | 51900 | UP
 v_vmart_node0001 | UDxSideProcess   | localhost.localdoma-27465:0x800b |  5677 | 44123 | UP

以下是上一个查询中返回的隔离进程的相应日志文件:

2016-05-16 11:24:43.990 [C++-localhost.localdoma-27465:0x800b-5677]  0x2b3ff17e7fd0 UDx side process started
 11:24:43.996 [C++-localhost.localdoma-27465:0x800b-5677]  0x2b3ff17e7fd0 Finished setting up signal handlers.
 11:24:43.996 [C++-localhost.localdoma-27465:0x800b-5677]  0x2b3ff17e7fd0 My port: 44123
 11:24:43.996 [C++-localhost.localdoma-27465:0x800b-5677]  0x2b3ff17e7fd0 My address: 0.0.0.0
 11:24:43.996 [C++-localhost.localdoma-27465:0x800b-5677]  0x2b3ff17e7fd0 Vertica port: 51900
 11:24:43.996 [C++-localhost.localdoma-27465:0x800b-5677]  0x2b3ff17e7fd0 Vertica address: 127.0.0.1
 11:25:19.749 [C++-localhost.localdoma-27465:0x800b-5677]  0x41837940 Setting memory resource limit to -1
 11:30:11.523 [C++-localhost.localdoma-27465:0x800b-5677]  0x41837940 Exiting UDx side process

最后一行指示从属进程已终止。在此示例中,该从属进程在用户会话 (vsql) 关闭时终止。

关于隔离模式配置参数

隔离模式支持以下配置参数

  • FencedUDxMemoryLimitMB:用于隔离模式进程的最大内存大小(以 MB 为单位)。默认值为 -1(无限制)。当超过此限制时,将终止从属进程。

  • ForceUDxFencedMode:如果将此参数设置为 1,将强制所有支持隔离模式的 UDx 在隔离模式下运行,即使其定义指定了 NOT FENCED 也如此。默认值为 0(禁用)。

  • UDxFencedBlockTimeout:Vertica 服务器在因 ERROR 3399 中止之前等待 UDx 返回的最长时间(以秒为单位)。默认值为 60

另请参阅

5 - 更新 UDx 库

在以下两种情况下,您需要更新已部署的库:

  • 您已将 Vertica 升级到包含 SDK API 更改的新版本。要使库与新的服务器版本正常使用,您需要使用新版本的 SDK 重新编译这些库。有关详细信息,请参阅UDx 库与新服务器版本的兼容性

  • 您已对 UDx 进行了更改,并且想要部署这些更改。更新 UDx 库之前,您需要确定是否更改了库中包含的任何函数的签名。如果是的话,则在更新库之前,您需要从 Vertica 编录中删除这些函数。

5.1 - UDx 库与新服务器版本的兼容性

Vertica SDK 定义了一个应用程序编程接口 (Application Programming Interface, API),UDx 可使用该 API 与数据库交互。当开发人员编译其 UDx 代码时,该代码会链接到 SDK 代码以形成一个库。此库仅与支持用于编译该代码的 SDK API 版本的 Vertica 服务器兼容。使用相同 API 版本的库和服务器在二进制级别是兼容的(此兼容性称为“二进制兼容”)。

如果您尝试加载与 Vertica 服务器不具有二进制兼容性的库,服务器会返回错误消息。同样,如果将 Vertica 服务器升级到支持新的 SDK API 的版本,任何依赖新的不兼容库的现有 UDx 会在您向其发出调用时返回错误消息:

ERROR 2858:  Could not find function definition
HINT:
This usually happens due to missing or corrupt libraries, libraries built
with the wrong SDK version, or due to a concurrent session dropping the library
or function. Try recreating the library and function

要解决此问题,您必须安装已使用正确的 SDK 版本重新编译的 UDx 库。

新版本的 Vertica 服务器并不总会更改 SDK API 版本。只要 Micro Focus 更改了构成 SDK 的组件,SDK API 版本就会更改。如果 SDK API 在新版本的服务器中未更改,旧库会继续与新服务器兼容。

当 Micro Focus 扩展 SDK 的功能时,SDK API 几乎总是在 Vertica 版本(主要版本、次要版本、服务包)中发生更改。Vertica 绝不会在修补程序补丁中更改 API。

这些策略意味着您必须在主要版本之间升级时更新 UDx 库。例如,如果从版本 10.0 升级到 10.1,则必须更新 UDx 库。

升级前步骤

升级 Vertica 服务器之前,应考虑是否存在任何与新版本不兼容的 UDx 库。请参阅新的服务器版本的发行说明,以确定 SDK API 在当前已安装的 Vertica 服务器版本和新版本之间是否已更改。如前文所述,只有从前一个主要版本升级或从某个主要版本的初始发行版升级到某个服务包发行版时,才会导致当前已加载的 UDx 库变为与服务器不兼容。

必须重新编译与新版本 Vertica 服务器不兼容的任何 UDx 库。如果 UDx 库之前是从第三方获取的,您必须检查该库是否已发布新版本。如果是这样,请在升级服务器后部署新版本(请参阅部署 UDx 库的新版本)。

如果 UDx 是您自己开发的(或者如果您有源代码),则您必须执行下列操作:

  1. 使用新版本的 Vertica SDK 重新编译 UDx 库。有关详细信息,请参阅编译 C++ 库编译并打包 Java 库

  2. 部署新版本的库。请参阅部署 UDx 库的新版本

5.2 - 确定 UDx 签名是否已更改

对包含已部署到 Vertica 数据库的函数的 UDx 库进行更改时,您需要小心操作。部署新版本的 UDx 库时,Vertica 无法确保在该库中定义的函数的签名与已在 Vertica 编录中定义的函数的签名匹配。如果您更改了库中的 UDx 的签名,然后在 Vertica 数据库中更新了该库,则对已更改的 UDx 发出的调用会生成错误。

对 UDx 进行以下任何更改会更改其签名:

  • 更改函数(不包括多态函数)所接受的参数个数,或更改所接受的任何参数的数据类型。

  • 更改任何返回值或输出列的个数或数据类型。

  • 更改 Vertica 用来为函数代码创建实例的工厂类的名称。

  • 更改函数的空值处理行为或可变性行为。

  • 从库中完成移除函数的工厂类。

以下更改不会更改函数的签名,也不要求在更新库之前删除函数。

  • 更改由多态函数处理的参数的个数或类型。Vertica 不会处理用户传递到多态函数的实参。

  • 更改函数所接受的参数的名称、数据类型或个数。函数所接受的参数不由函数签名决定。相反,Vertica 会传递用户包含到函数调用中的所有函数,而函数会在运行时传递这些参数。有关参数的详细信息,请参阅 UDx 参数

  • 更改由函数执行的任何内部处理。

  • 将新的 UDx 添加到库中

删除签名已更改的任何函数之后,您可以加载新的库文件,然后重新创建已更改的函数。如果未对 UDx 的签名进行任何更改,则您只需在 Vertica 数据库中更新库文件即可,而无需删除或更改函数定义。只要 Vertica 编录中的 UDx 定义与库中的函数的签名匹配,函数调用就可以在更新库之后以透明方式工作。请参阅部署 UDx 库的新版本

5.3 - 部署 UDx 库的新版本

在下列情况下,您需要部署 UDx 库的新版本:

  • 您对库进行了更改,并且现在想要将这些更改应用到 Vertica 数据库。

  • 您已将 Vertica 升级到新版本,并且该新版本的 SDK 与上一个版本不兼容。

部署库的新版本的过程与初始部署相似。

  1. 如果要部署在 C++ 或 Java 中开发的 UDx 库,您必须使用当前版本的 Vertica SDK 编译该库。

  2. 将 UDx 的库文件(对于在 C++ 中开发的库,此文件是 .so 文件;对于在 Python 中开发的库,此文件是 .py 文件;对于在 Java 中开发的库,此文件是 .jar 文件)或 R 源文件复制到 Vertica 数据库中的主机。

  3. 使用 vsql 连接到主机。

  4. 如果更改了共享库中任何 UDx 的签名,您必须使用 DROP 语句(例如 DROP FUNCTIONDROP SOURCE)删除它们。如果不确定函数的任何签名是否已更改,请参阅确定 UDx 签名是否已更改

  5. 通过 ALTER LIBRARY 语句使用在步骤 1 中复制的文件更新 UDx 库定义。例如,如果要使用 dbadmin 用户主目录中名为 ScalarFunctions-2.0.so 的文件更新名为 ScalarFunctions 的库,请使用以下命令:

    => ALTER LIBRARY ScalarFunctions AS '/home/dbadmin/ScalarFunctions-2.0.so';
    

    更新了 UDx 库定义以使用新版本的共享库之后,使用 UDx 库中的类定义的 UDx 将开始使用新的共享库文件,而不进一步对该文件进行任何更改。

  6. 如果在步骤 4 中必须删除任何函数,请使用由库中的工厂类定义的新签名重新创建这些函数。请参阅CREATE FUNCTION 语句

6 - 列出包含在库中的 UDx

使用 CREATE LIBRARY 语句加载了库之后,您可以通过查询 USER_LIBRARY_MANIFEST 系统表来查找 UDx 及其包含的 UDL:

=> CREATE LIBRARY ScalarFunctions AS '/home/dbadmin/ScalarFunctions.so';
CREATE LIBRARY
=> \x
Expanded display is on.
=> SELECT * FROM USER_LIBRARY_MANIFEST WHERE lib_name = 'ScalarFunctions';
-[ RECORD 1 ]-------------------
schema_name | public
lib_name    | ScalarFunctions
lib_oid     | 45035996273792402
obj_name    | RemoveSpaceFactory
obj_type    | Scalar Function
arg_types   | Varchar
return_type | Varchar
-[ RECORD 2 ]-------------------
schema_name | public
lib_name    | ScalarFunctions
lib_oid     | 45035996273792402
obj_name    | Div2intsInfo
obj_type    | Scalar Function
arg_types   | Integer, Integer
return_type | Integer
-[ RECORD 3 ]-------------------
schema_name | public
lib_name    | ScalarFunctions
lib_oid     | 45035996273792402
obj_name    | Add2intsInfo
obj_type    | Scalar Function
arg_types   | Integer, Integer
return_type | Integer

obj_name 列将列出库中包含的工厂类。您可以使用这些名称通过诸如 CREATE FUNCTIONCREATE SOURCE 等语句在数据库编录中定义 UDx 和 UDL。

7 - 在您的 UDx 中使用通配符

Vertica 支持在用户定义的函数中使用通配符 * 来替代列名称。

您可以在以下情况下使用通配符:

  • 您的查询在 FROM 子句中包含表

  • 您正在使用支持 Vertica 的开发语言

  • 您的 UDx 正在以隔离模式或非隔离模式运行

支持的 SQL 语句

以下 SQL 语句可以接受通配符:

  • DELETE

  • INSERT

  • SELECT

  • UPDATE

不支持的配置

以下情况不支持通配符:

  • 不能在查询的 OVER 子句中传递通配符

  • 不能在 DROP 语句中使用通配符

  • 不能将通配符与任何其他参数结合使用

示例

以下示例显示了一系列数据处理操作中的通配符和用户定义的函数。

DELETE 语句:

=> DELETE FROM tablename WHERE udf(tablename.*) = 5;

INSERT 语句:

=> INSERT INTO table1 SELECT udf(*) FROM table2;

SELECT 语句:

=> SELECT udf(*) FROM tablename;
=> SELECT udf(tablename.*) FROM tablename;
=> SELECT udf(f.*) FROM table f;
=> SELECT udf(*) FROM table1,table2;
=> SELECT udf1( udf2(*) ) FROM table1,table2;
=> SELECT udf( db.schema.table.*) FROM tablename;
=> SELECT udf(sub.*) FROM (select col1, col2 FROM table) sub;
=> SELECT x FROM tablename WHERE udf(*) = y;
=> WITH sub as (SELECT * FROM tablename) select x, udf(*) FROM sub;
=> SELECT udf( * using parameters x=1) FROM tablename;
=> SELECT udf(table1.*, table2.col2) FROM table1,table2;

UPDATE 语句:

=> UPDATE tablename set col1 = 4 FROM tablename WHERE udf(*) = 3;