C++ 示例:字符串分词器
以下示例显示了 TransformFunction
的子类,它名为 StringTokenizer
。此子类定义的 UDTF 可读取包含 INTEGER ID 列和 VARCHAR 列的表。此子类可将 VARCHAR 列中的文本拆分为标记(各个词)。此子类将返回一个表,表中包含每个标记、标记所出现在的行以及标记在字符串中的位置。
加载和使用示例
以下示例显示了如何将函数加载至 Vertica 中。假设包含该函数的 TransformFunctions.so
库已复制到启动程序节点上数据库管理员用户的主目录中。
=> CREATE LIBRARY TransformFunctions AS
'/home/dbadmin/TransformFunctions.so';
CREATE LIBRARY
=> CREATE TRANSFORM FUNCTION tokenize
AS LANGUAGE 'C++' NAME 'TokenFactory' LIBRARY TransformFunctions;
CREATE TRANSFORM FUNCTION
然后,您可以通过 SQL 语句使用该函数,例如:
=> CREATE TABLE T (url varchar(30), description varchar(2000));
CREATE TABLE
=> INSERT INTO T VALUES ('www.amazon.com','Online retail merchant and provider of cloud services');
OUTPUT
--------
1
(1 row)
=> INSERT INTO T VALUES ('www.vertica.com','World''s fastest analytic database');
OUTPUT
--------
1
(1 row)
=> COMMIT;
COMMIT
=> -- Invoke the UDTF
=> SELECT url, tokenize(description) OVER (partition by url) FROM T;
url | words
-----------------+-----------
www.amazon.com | Online
www.amazon.com | retail
www.amazon.com | merchant
www.amazon.com | and
www.amazon.com | provider
www.amazon.com | of
www.amazon.com | cloud
www.amazon.com | services
www.vertica.com | World's
www.vertica.com | fastest
www.vertica.com | analytic
www.vertica.com | database
(12 rows)
请注意,结果表中的行数和列数与输入表中不同。这是 UDTF 的优势之一。
TransformFunction 实施
以下代码显示了 StringTokenizer
类。
class StringTokenizer : public TransformFunction
{
virtual void processPartition(ServerInterface &srvInterface,
PartitionReader &inputReader,
PartitionWriter &outputWriter)
{
try {
if (inputReader.getNumCols() != 1)
vt_report_error(0, "Function only accepts 1 argument, but %zu provided", inputReader.getNumCols());
do {
const VString &sentence = inputReader.getStringRef(0);
// If input string is NULL, then output is NULL as well
if (sentence.isNull())
{
VString &word = outputWriter.getStringRef(0);
word.setNull();
outputWriter.next();
}
else
{
// Otherwise, let's tokenize the string and output the words
std::string tmp = sentence.str();
std::istringstream ss(tmp);
do
{
std::string buffer;
ss >> buffer;
// Copy to output
if (!buffer.empty()) {
VString &word = outputWriter.getStringRef(0);
word.copy(buffer);
outputWriter.next();
}
} while (ss);
}
} while (inputReader.next() && !isCanceled());
} catch(std::exception& e) {
// Standard exception. Quit.
vt_report_error(0, "Exception while processing partition: [%s]", e.what());
}
}
};
此示例中的 processPartition()
函数将遵循您在自己的 UDTF 中遵循的相同模式:遍历 Vertica 向其发送的表分区中的所有行,以处理每个行并在前进之前检查是否已取消查询。对于 UDTF,您实际上不必处理每个行。即使退出函数而不读取所有输入,也不会出现任何问题。如果 UDTF 在执行某种搜索或其他某项操作后确定其余资源是不需要的,您可以选择执行此操作。
在此示例中,processPartition()
会先从 PartitionReader
对象中提取包含文本的 VString
。VString
类代表 Vertica 字符串值(VARCHAR 或 CHAR)。如果存在输入,它会对其进行字符串标记并使用 PartitionWriter
对象将其添加到输出中。
与读取输入列类似,PartitionWriter
类具有将每种类型的数据写入输出行的函数。在这种情况下,该示例会调用 PartitionWriter
对象的 getStringRef()
函数来分配新的 VString
对象以保存第一列的输出标记,然后将标记的值复制到 VString
中。
TransformFunctionFactory 实施
以下代码显示了该工厂类。
class TokenFactory : public TransformFunctionFactory
{
// Tell Vertica that we take in a row with 1 string, and return a row with 1 string
virtual void getPrototype(ServerInterface &srvInterface, ColumnTypes &argTypes, ColumnTypes &returnType)
{
argTypes.addVarchar();
returnType.addVarchar();
}
// Tell Vertica what our return string length will be, given the input
// string length
virtual void getReturnType(ServerInterface &srvInterface,
const SizedColumnTypes &inputTypes,
SizedColumnTypes &outputTypes)
{
// Error out if we're called with anything but 1 argument
if (inputTypes.getColumnCount() != 1)
vt_report_error(0, "Function only accepts 1 argument, but %zu provided", inputTypes.getColumnCount());
int input_len = inputTypes.getColumnType(0).getStringLength();
// Our output size will never be more than the input size
outputTypes.addVarchar(input_len, "words");
}
virtual TransformFunction *createTransformFunction(ServerInterface &srvInterface)
{ return vt_createFuncObject<StringTokenizer>(srvInterface.allocator); }
};
在此示例中:
-
UDTF 会将 VARCHAR 列作为输入。为了定义输入列,
getPrototype()
会在表示输入表的ColumnTypes
对象上调用addVarchar()
。 -
UDTF 将返回 VARCHAR 作为输出。
getPrototype()
函数会调用addVarchar()
以定义输出表。
此示例必须返回 VARCHAR 输出列的最大长度。它会将长度设置为输入字符串的长度。这是一个安全值,因为输出长度永远不会超过输入字符串。此示例还会将 VARCHAR 输出列的名称设置为“words”。
注意
虽然您不必在此函数中为输出列提供名称,但这是最佳实践。如果未给输出列命名,getReturnType()
会将列名称设置为 ""。调用 UDTF 的 SQL 语句必须为任何未命名的列提供别名才能访问这些列,否则会返回错误。从可用性的角度来说,通过在此处提供列名称,以后就不必再次提供,从而减少麻烦。备选方法是强制函数的所有用户在每次调用 UDTF 时提供自己的列名称。
此示例中的 createTransformFunction()
函数实施是样板代码。该实施仅使用与此工厂类关联的 TransformFunction
类的名称来调用 vt_returnFuncObj
宏。此宏负责将 TransformFunction
类的副本实例化,Vertica 可以使用该副本来处理数据。
RegisterFactory 宏
创建 UDTF 的最后一步是调用 RegisterFactory
宏。此宏可确保在 Vertica 加载包含 UDTF 的共享库时,工厂类已实例化。只有将工厂类初始化,Vertica 才能找到 UDTF 并确定其输入和输出,除此之外没有任何其他方法。
RegisterFactory
宏仅使用工厂类的名称:
RegisterFactory(TokenFactory);