实参和返回值
对于除加载 (UDL) 之外的所有 UDx 类型,工厂类会声明关联函数的实参和返回类型。为此,工厂有两种方法:
-
getPrototype()
(必需):声明输入和输出类型 -
getReturnType()
(有时必需):声明返回类型,包括长度和精度(适用时)
getPrototype()
方法会收到两个 ColumnTypes
参数,一个用于输入,一个用于输出。C++ 示例:字符串分词器 中的工厂接受单个输入字符串并返回字符串:
virtual void getPrototype(ServerInterface &srvInterface,
ColumnTypes &argTypes, ColumnTypes &returnType)
{
argTypes.addVarchar();
returnType.addVarchar();
}
ColumnTypes
类为每种受支持的类型提供“add”方法,例如 addVarchar()
。此类支持具有 addArrayType()
和 addRowType()
方法的复杂类型;请参阅作为实参的复杂类型。如果函数为多态函数,则可以改为调用 addAny()
。然后,您负责验证输入和输出。有关实施多态 UDx 的详细信息,请参阅创建多态 UDx。
getReturnType()
方法会计算返回值的最大长度。如果 Udx 返回特定大小的列(一种长度可变的返回数据类型,例如 VARCHAR)、需要精度的值或多个值,则需要实施此工厂方法。(某些 UDx 类型要求您实施该方法。)
输入是 SizedColumnTypes
,其中包含输入实参类型及其长度。根据输入类型,请将以下值之一添加到输出类型:
-
CHAR、(LONG) VARCHAR、BINARY 和 (LONG) VARBINARY:返回最大长度。
-
NUMERIC 类型:指定精度和小数位数。
-
TIME 和 TIMESTAMP 值(含或不含时区):指定精度。
-
INTERVAL YEAR TO MONTH:指定范围。
-
INTERVAL DAY TO SECOND:指定精度和范围。
-
ARRAY:指定数组元素的最大数量。
使用字符串分词器时,输出为 VARCHAR 且函数可确定其最大长度:
// 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");
}
作为实参的复杂类型和返回值
ColumnTypes
类支持 ARRAY 和 ROW 类型。数组包含各种元素,而行包含各种字段,两者均包含您需要描述的类型。要使用复杂类型,您需要为数组或行构建 ColumnTypes
对象,然后将其添加到表示函数输入和输出的 ColumnTypes
对象中。
在以下示例中,转换函数的输入是一个订单数组(即多个行),输出是各个行及其在数组中的位置。订单包含送货地址 (VARCHAR) 和产品 ID 数组 (INT)。
工厂的 getPrototype()
方法会先为数组和行元素创建 ColumnTypes
,然后使用它们来调用 addArrayType()
和 addRowType()
:
void getPrototype(ServerInterface &srv,
ColumnTypes &argTypes,
ColumnTypes &retTypes)
{
// item ID (int), to be used in an array
ColumnTypes itemIdProto;
itemIdProto.addInt();
// row: order = address (varchar) + array of previously-created item IDs
ColumnTypes orderProto;
orderProto.addVarchar(); /* address */
orderProto.addArrayType(itemIdProto); /* array of item ID */
/* argument (input) is array of orders */
argTypes.addArrayType(orderProto);
/* return values: index in the array, order */
retTypes.addInt(); /* index of element */
retTypes.addRowType(orderProto); /* element return type */
}
实参包括特定大小的类型 (VARCHAR)。getReturnType()
方法会使用类似的方法,从而使用 Fields
类在订单中构建两个字段。
void getReturnType(ServerInterface &srv,
const SizedColumnTypes &argTypes,
SizedColumnTypes &retTypes)
{
Fields itemIdElementFields;
itemIdElementFields.addInt("item_id");
Fields orderFields;
orderFields.addVarchar(32, "address");
orderFields.addArrayType(itemIdElementFields[0], "item_id");
// optional third arg: max length, default unbounded
/* declare return type */
retTypes.addInt("index");
static_cast<Fields &>(retTypes).addRowType(orderFields, "element");
/* NOTE: presumably we have verified that the arguments match the prototype, so really we could just do this: */
retTypes.addInt("index");
retTypes.addArg(argTypes.getColumnType(0).getElementType(), "element");
}
要在 UDx 处理方法中访问复杂类型,请使用 ArrayReader
、ArrayWriter
、StructReader
和 StructWriter
类。
请参阅“ C++ 示例:使用复杂类型”,了解使用数组的多态函数。
工厂的 getPrototype()
方法会先使用
makeType()
和
addType()
方法,为行及其元素创建和构造 ColumnTypes
。然后,该方法会调用
addType()
方法,以将这些构造的 ColumnTypes
添加到 arg_types
和 return_type
对象:
def getPrototype(self, srv_interface, arg_types, return_type):
# item ID (int), to be used in an array
itemIdProto = vertica_sdk.ColumnTypes.makeInt()
# row (order): address (varchar) + array of previously-created item IDs
orderProtoFields = vertica_sdk.ColumnTypes.makeEmpty()
orderProtoFields.addVarchar() # address
orderProtoFields.addArrayType(itemIdProto) # array of item ID
orderProto = vertica_sdk.ColumnTypes.makeRowType(orderProtoFields)
# argument (input): array of orders
arg_types.addArrayType(orderProto)
# return values: index in the array, order
return_type.addInt(); # index of element
return_type.addRowType(orderProto); # element return type
工厂的 getReturnType()
方法会使用 makeInt()
和 makeEmpty()
方法来创建 SizedColumnTypes
,然后使用 addVarchar()
和 addArrayType()
方法构建两个行字段。请注意,addArrayType()
方法会将数组元素的最大数量指定为 1024。 getReturnType()
然后,将这些构造的 SizedColumnTypes
添加到表示返回类型的对象中。
def getReturnType(self, srv_interface, arg_types, return_type):
itemsIdElementField = vertica_sdk.SizedColumnTypes.makeInt("item_id")
orderFields = vertica_sdk.SizedColumnTypes.makeEmpty()
orderFields.addVarchar(32, "address")
orderFields.addArrayType(itemIdElementField, 1024, "item_ids")
# declare return type
return_type.addInt("index")
return_type.addRowType(orderFields, "element")
'''
NOTE: presumably we have verified that the arguments match the prototype, so really we could just do this:
return_type.addInt("index")
return_type.addArrayType(argTypes.getColumnType(0).getElementType(), "element")
'''
要在 UDx 处理方法中访问复杂类型,请使用 ArrayReader
、ArrayWriter
、RowReader
和 RowWriter
类。有关详细信息,请参阅Python SDK。
有关使用复杂类型的标量函数,请参阅Python 示例:矩阵乘法。
处理不同数量和类型的实参
使用过载或多态性,您可以创建处理多个签名的 UDx,甚至还可以创建接受由用户提供给它们的所有实参的 UDx。
您可以使 UDx 过载,方法是将同一个 SQL 函数名称分配给多个工厂类,每个工厂类会定义一个唯一的函数签名。当用户在查询中使用该函数名称时,Vertica 会尝试将函数调用的签名与工厂的 getPrototype()
方法所声明的签名进行匹配。如果 UDx 需要接受几个不同签名(例如,接受两个必需参数和一个可选参数),则这是可用的最佳技术。
或者,您可以编写一个多态函数,以写入一个工厂方法而非多个工厂方法,并声明它接受任何数量和类型的实参。当用户在查询中使用函数名称时,Vertica 会调用您的函数,而不考虑签名。为换取这种灵活性,UDx 的主要“process”方法必须确定它是否可以接受各种实参并在无法接受时发出错误。
所有 UDx 类型都可以使用多态输入。转换函数和分析函数也可以使用多态输出。这意味着 getPrototype()
可以声明返回类型“any”并在运行时设置实际返回类型。例如,在输入中返回最大值的函数将返回与输入类型相同的类型。