Greenplum Partner Connector API

Greenplum Partner Connector API

使用Greenplum Partner Connector API(GPPC API), 您可以使用C和C++编程语言编写可移植的Greenplum数据库用户定义函数(UDF)。 使用GPPC API开发的函数不需要重新编译或修改即可使用较旧或较新的Greenplum数据库版本。

可以使用Greenplum数据库中的SQL调用您写入GPPC API的函数。 API提供了一组函数和宏,可用于通过服务器编程接口(SPI)发出SQL命令,操作简单和复合数据类型函数参数和返回值,管理内存和处理数据。

您将使用GPPC API开发的C/C++函数编译到共享库中。 在Greenplum数据库群集中安装共享库并将GPPC函数注册为SQL UDF之后,Greenplum数据库用户可以使用GPPC函数。

Note: Greenplum Partner Connector支持Greenplum数据库版本4.3.5.0及更高版本。

本主题包含以下信息:

使用GPPC API

GPPC API与PostgreSQL定义的C语言函数共享一些概念。 有关开发C语言函数的详细信息,请参阅PostgreSQL文档中的C语言函数

GPPC API是一个包装器,它使C/C++函数可以在Greenplum数据库中调用SQL。 这个包装器通过API定义的函数和宏对表和数据操作以及SPI操作进行规范化来屏蔽您从Greenplum数据库更改中编写的GPPC函数。

GPPC API包括以下函数和宏:

  • 对基础和复合数据类型进行操作。
  • 处理函数参数和返回值。
  • 分配和释放内存。
  • 记录并向客户报告错误。
  • 发出SPI查询。
  • 返回一个表或一组行。
  • 将表作为函数输入参数。

要求

使用GPPC API进行开发时:

  • 您必须在具有与Greenplum数据库主机相同的硬件和软件体系结构的系统上开发代码。
  • 您必须使用C或C++编程语言编写GPPC函数。
  • 函数代码必须使用GPPC API,数据类型和宏。
  • 函数代码不得使用PostgreSQL C语言函数API,头文件,函数或宏。
  • 函数代码不能#include postgres.h头文件或使用PG_MODULE_MAGIC
  • 您必须仅使用GPPC包装的内存函数来分配和释放内存。 请参阅内存处理
  • 目标文件中的符号名称不得相互冲突,也不得与Greenplum数据库服务器中定义的符号冲突。 如果收到此类错误消息,则必须重命名函数或变量。

头文件和库文件

GPPC头文件和库文件安装在$GPHOME中:

  • $GPHOME/include/gppc.h - 主要的GPPC头文件
  • $GPHOME/include/gppc_config.h - 定义GPPC版本的头文件
  • $GPHOME/lib/libgppc.[a, so, so.1, so.1.2] - GPPC存档和共享库

数据类型

您创建的GPPC函数将对驻留在Greenplum数据库中的数据进行操作。 GPPC API包含等效Greenplum数据库SQL数据类型的数据类型定义。 您必须在GPPC函数中使用这些类型。

GPPC API定义了可用于表示任何GPPC类型的通用数据类型。 此数据类型名为GppcDatum,定义如下:
typedef int64_t GppcDatum;

下表标识了每个GPPC数据类型以及它映射到的SQL类型。

SQL类型 GPPC类型 GPPC类型的Oid
boolean GppcBool GppcOidBool
char (single byte) GppcChar GppcOidChar
int2/smallint GppcInt2 GppcOidInt2
int4/integer GppcInt4 GppcOidInt4
int8/bigint GppcInt8 GppcOidInt8
float4/real GppcFloat4 GppcOidFloat4
float8/double GppcFloat8 GppcOidFloat8
text *GppcText GppcOidText
varchar *GppcVarChar GppcOidVarChar
char *GppcBpChar GppcOidBpChar
bytea *GppcBytea GppcOidBytea
numeric *GppcNumeric GppcOidNumeric
date GppcDate GppcOidDate
time GppcTime GppcOidTime
timetz *GppcTimeTz GppcOidTimeTz
timestamp GppcTimestamp GppcOidTimestamp
timestamptz GppcTimestampTz GppcOidTimestampTz
anytable GppcAnyTable GppcOidAnyTable
oid GppcOid  

GPPC API专门处理文本,数字和时间戳数据类型,提供对这些类型进行操作的函数。

示例GPPC基本数据类型声明:
GppcText       message;
GppcInt4       arg1;
GppcNumeric    total_sales;
GPPC API定义了在通用GppcDatum类型和GPPC特定类型之间进行转换的函数。 例如,要从整数转换为datum:
GppcInt4 num = 13;
GppcDatum num_dat = GppcInt4GetDatum(num);

符合类型

复合数据类型表示行或记录的结构,由字段名称列表及其数据类型组成。 该结构信息通常被称为元组描述符。 复合类型的实例通常称为元组或行。 元组没有固定的布局,可以包含空字段。

GPPC API提供了一个界面,您可以使用该界面定义元组的结构,访问和设置元组。 当GPPC函数将表作为输入参数或返回表或记录类型集时,将使用此接口。 本主题后面将介绍使用表中的元组和设置返回函数。

函数声明,参数和结果

GPPC API依赖于宏来声明函数并简化函数参数和结果的传递。 这些宏包括:

任务 宏签名 描述
使函数SQL-可调用 GPPC_FUNCTION_INFO(function_name) 粘贴使函数function_nameSQL-可调用。
声明一个函数 GppcDatum function_name(GPPC_FUNCTION_ARGS) 声明名为function_name的GPPC函数; 每个函数都必须具有相同的签名。
返回参数的数量 GPPC_NARGS() 返回传递给函数的参数数量。
获取参数 GPPC_GETARG_<ARGTYPE>(arg_num) 获取参数编号arg_num的值(从0开始),其中<ARGTYPE>标识参数的数据类型。 例如,GPPC_GETARG_FLOAT8(0)
获取并创建文本类型参数的副本 GPPC_GETARG_<ARGTYPE>_COPY(arg_num) 获取并复制参数号arg_num的值(从0开始)。 <ARGTYPE>标识文本类型(text,varchar,bpchar,bytea)。 例如,GPPC_GETARG_BYTEA_COPY(1)
确定参数是否为NULL GPPC_ARGISNULL(arg_num) 返回参数编号arg_num是否为NULL。
返回结果 GPPC_RETURN_<ARGTYPE>(return_val) 返回值return_val,其中<ARGTYPE>标识返回值的数据类型。 例如,GPPC_RETURN_INT4(131)

定义和实现GPPC函数时,必须使用上面标识的两个声明使用GPPC API声明它。 例如,要声明名为add_int4s()的GPPC函数:
GPPC_FUNCTION_INFO(add_int4s);
GppcDatum add_int4s(GPPC_FUNCTION_ARGS);

GppcDatum
add_int4s(GPPC_FUNCTION_ARGS)
{
  // code here
}
如果add_int4s()函数接受两个int4类型的输入参数,则使用GPPC_GETARG_INT4(arg_num)宏来访问参数值。 参数索引从0开始。例如:
GppcInt4  first_int = GPPC_GETARG_INT4(0);
GppcInt4  second_int = GPPC_GETARG_INT4(1);
如果add_int4s()返回两个输入参数的总和,则使用GPPC_RETURN_INT8(return_val)宏来返回此总和。 例如:
GppcInt8  sum = first_int + second_int;
GPPC_RETURN_INT8(sum);
完整的GPPC函数:
GPPC_FUNCTION_INFO(add_int4s);
GppcDatum add_int4s(GPPC_FUNCTION_ARGS);

GppcDatum
add_int4s(GPPC_FUNCTION_ARGS)
{
  // get input arguments
  GppcInt4    first_int = GPPC_GETARG_INT4(0);
  GppcInt4    second_int = GPPC_GETARG_INT4(1);

  // add the arguments
  GppcInt8    sum = first_int + second_int;

  // return the sum
  GPPC_RETURN_INT8(sum);
}

内存处理

GPPC API提供用于分配和释放内存的函数,包括文本内存。 必须将这些函数用于所有内存操作。

函数名 描述
void *GppcAlloc( size_t num ) 分配num个字节的未初始化内存。
void *GppcAlloc0( size_t num ) 分配num个字节的初始化为0内存。
void *GppcRealloc( void *ptr, size_t num ) 调整预分配的内存大小。
void GppcFree( void *ptr ) 释放分配的内存。

分配内存后,可以使用memcpy()等系统函数来设置数据。

以下示例分配GppcDatum数组并将数组设置为函数输入参数的datum版本:
GppcDatum  *values;
int attnum = GPPC_NARGS();

// allocate memory for attnum values
values = GppcAlloc( sizeof(GppcDatum) * attnum );

// set the values
for( int i=0; i<attnum; i++ ) {
    GppcDatum d = GPPC_GETARG_DATUM(i);
    values[i] = d;
}

为GPPC函数分配内存时,可以在当前上下文中分配它。 GPPC API包括返回,创建,切换和重置内存上下文的函数。

函数名 描述
GppcMemoryContext GppcGetCurrentMemoryContext(void) 返回当前内存上下文。
GppcMemoryContext GppcMemoryContextCreate(GppcMemoryContext parent) parent下创建新的内存上下文。
GppcMemoryContext GppcMemoryContextSwitchTo(GppcMemoryContext context) 切换到内存上下文context
void GppcMemoryContextReset(GppcMemoryContext context) 在内存上下文context中重置(释放)内存。

Greenplum数据库通常在每元组上下文中调用一个SQL调用的函数,它在每次服务器后端处理表行时创建和删除。 不要假设在当前内存上下文中分配的内存在多个函数调用中可用。

使用可变长度文本类型

GPPC API支持可变长度文本,varchar,空白填充和字节数组类型。 在对这些数据类型进行操作时,必须使用GPPC API提供的函数。 GPPC API中提供的可变文本操作函数包括为其分配内存,确定字符串长度,获取字符串指针以及访问这些类型的函数:

函数名 描述
GppcText GppcAllocText( size_t len )

GppcVarChar GppcAllocVarChar( size_t len )

GppcBpChar GppcAllocBpChar( size_t len )

GppcBytea GppcAllocBytea( size_t len )

为不同长度类型分配len个字节的内存。
size_t GppcGetTextLength( GppcText s )

size_t GppcGetVarCharLength( GppcVarChar s )

size_t GppcGetBpCharLength( GppcBpChar s )

size_t GppcGetByteaLength( GppcBytea b )

返回内存块中的字节数。
char *GppcGetTextPointer( GppcText s )

char *GppcGetVarCharPointer( GppcVarChar s )

char *GppcGetBpCharPointer( GppcBpChar s )

char *GppcGetByteaPointer( GppcBytea b )

返回一个指向内存块头部的字符串指针。该字符串不以空值终止。
char *GppcTextGetCString( GppcText s )

char *GppcVarCharGetCString( GppcVarChar s )

char *GppcBpCharGetCString( GppcBpChar s )

返回一个指向内存块头部的字符串指针。该字符串以空值终止。
GppcText *GppcCStringGetText( const char *s )

GppcVarChar *GppcCStringGetVarChar( const char *s )

GppcBpChar *GppcCStringGetBpChar( const char *s )

从字符串构建变长类型。

GppcGet<VLEN_ARGTYPE>Pointer()函数返回的内存可能指向实际的数据库内容。 请勿修改内存内容。 GPPC API提供了在需要时为这些类型分配内存的函数。 分配内存后,可以使用memcpy()等系统函数来设置数据。

以下示例处理文本输入参数,并为文本字符串连接操作分配和设置结果内存:
GppcText first_textstr = GPPC_GETARG_TEXT(0);
GppcText second_textstr = GPPC_GETARG_TEXT(1);

// determine the size of the concatenated string and allocate
// text memory of this size
size_t arg0_len = GppcGetTextLength(first_textstr);
size_t arg1_len = GppcGetTextLength(second_textstr);
GppcText retstring = GppcAllocText(arg0_len + arg1_len);

// construct the concatenated return string; copying each string
// individually
memcpy(GppcGetTextPointer(retstring), GppcGetTextPointer(first_textstr), arg0_len);
memcpy(GppcGetTextPointer(retstring) + arg0_len, GppcGetTextPointer(second_textstr), arg1_len);

错误报告和记录

GPPC API提供错误报告和日志记录函数。 API定义的报告级别与Greenplum数据库中的级别相同:
typedef enum GppcReportLevel
{
        GPPC_DEBUG1                             = 10,
        GPPC_DEBUG2                             = 11,
        GPPC_DEBUG3                             = 12,
        GPPC_DEBUG4                             = 13,
        GPPC_DEBUG                              = 14,
        GPPC_LOG                                = 15,
        GPPC_INFO                               = 17,
        GPPC_NOTICE                             = 18,
        GPPC_WARNING                    	= 19,
        GPPC_ERROR                              = 20,
} GppcReportLevel;
(Greenplum数据库client_min_messages服务器配置参数控制当前客户端日志记录级别。 log_min_messages配置参数控制当前日志到日志文件级别。)

GPPC报告包括报告级别,报告消息和可选的报告回调函数。

GPPC API提供的报告和处理函数包括:

函数名 描述
GppcReport() 格式化并打印/记录指定报告级别的字符串。
GppcInstallReportCallback() 注册/安装报告回调函数。
GppcUninstallReportCallback() 卸载报告回调函数。
GppcGetReportLevel() 从错误报告中检索级别。
GppcGetReportMessage() 从错误报告中检索消息。
GppcCheckForInterrupts() 如果中断挂起则出错。

GppcReport()函数签名是:
void GppcReport(GppcReportLevel elevel, const char *fmt, ...);
GppcReport()采用类似于printf()的格式化字符串输入参数。 以下示例生成格式化GPPC文本参数的错误级别报告消息:
GppcText  uname = GPPC_GETARG_TEXT(1);
GppcReport(GPPC_ERROR, "Unknown user name: %s", GppcTextGetCString(uname));

有关示例报告回调处理程序,请参阅GPPC示例代码

SPI函数

Greenplum数据库服务器编程接口(SPI)为C/C++函数的编写者提供了在GPPC函数中运行SQL命令的能力。 有关SPI函数的其他信息,请参阅PostgreSQL文档中的服务器编程接口

GPPC API公开了PostgreSQL SPI函数的子集。 通过该子集,您可以在GPPC函数中发出SPI查询并检索SPI结果值。 GPPC SPI包装器函数是:

SPI函数名 GPPC函数名 描述
SPI_connect() GppcSPIConnect() 连接到Greenplum数据库服务器编程接口。
SPI_finish() GppcSPIFinish() 断开与Greenplum数据库服务器编程接口的连接。
SPI_exec() GppcSPIExec() 执行SQL语句,返回行数。
SPI_getvalue() GppcSPIGetValue() 从SQL结果中按编号检索特定属性的值作为字符串。
GppcSPIGetDatum() 从SQL结果中按编号检索特定属性的值作为GppcDatum
GppcSPIGetValueByName() 按名称从SQL结果中检索特定属性的值作为字符串。
GppcSPIGetDatumByName() 按名称从SQL结果中检索特定属性的值作为GppcDatum

创建访问服务器编程接口的GPPC函数时,您的函数应符合以下流程:
GppcSPIConnect();
GppcSPIExec(...)
// process the results - GppcSPIGetValue(...), GppcSPIGetDatum(...)
GppcSPIFinish()
您可以使用GppcSPIExec()在GPPC函数中执行SQL语句。 调用此函数时,还可以标识要返回的最大行数。 GppcSPIExec()的函数签名是:
GppcSPIResult GppcSPIExec(const char *sql_statement, long rcount);
GppcSPIExec()返回GppcSPIResult结构。 该结构表示SPI结果数据。 它包括指向数据的指针,有关处理的行数的信息,计数器和结果代码。 GPPC API定义此结构如下:
typedef struct GppcSPIResultData
{
    struct GppcSPITupleTableData   *tuptable;
    uint32_t                       processed;
    uint32_t                       current;
    int                            rescode;
} GppcSPIResultData;
typedef GppcSPIResultData *GppcSPIResult;

您可以设置和使用GppcSPIResult结构中的current字段来检查tuptable结果数据的每一行。

以下代码摘录使用GPPC API连接到SPI,执行简单查询,循环查询结果并完成处理:
GppcSPIResult   result;
char            *attname = "id";
char            *query = "SELECT i, 'foo' || i AS val FROM generate_series(1, 10)i ORDER BY 1";
bool            isnull = true;

// connect to SPI
if( GppcSPIConnect() < 0 ) {
    GppcReport(GPPC_ERROR, "cannot connect to SPI");
}

// execute the query, returning all rows
result = GppcSPIExec(query, 0);

// process result
while( result->current < result->processed ) {
    // get the value of attname column as a datum, making a copy
    datum = GppcSPIGetDatumByName(result, attname, &isnull, true);

    // do something with value

    // move on to next row
    result->current++;
}

// complete processing
GppcSPIFinish();

关于元组描述符和元组

表或一组记录包含一个或多个元组(行)。 元组的每个属性的结构由元组描述符定义。 元组描述符为元组中的每个属性定义以下内容:
  • 属性名称
  • 属性数据类型的对象标识符
  • 属性数据类型的字节长度
  • 属性修饰符的对象标识符

GPPC API定义了一个抽象类型GppcTupleDesc来表示元组/行描述符。 API还提供了可用于创建,访问和设置元组描述符的函数:

函数名称 描述
GppcCreateTemplateTupleDesc() 创建具有指定数量的属性的空元组描述符。
GppcTupleDescInitEntry() 在指定位置向元组描述符添加属性。
GppcTupleDescNattrs() 获取元组描述符中的属性数。
GppcTupleDescAttrName() 获取元组描述符中特定位置(从0开始)的属性名称。
GppcTupleDescAttrType() 获取元组描述符中特定位置(从0开始)的属性的类型对象标识符。
GppcTupleDescAttrLen() 获取元组描述符中特定位置(从0开始)的属性的类型长度。
GppcTupleDescAttrTypmod() 获取元组描述符中特定位置(从0开始)的属性的类型修饰符对象标识符。

要构造元组描述符,首先要创建一个模板,然后为每个属性填写描述符字段。 这些函数的签名是:
GppcTupleDesc GppcCreateTemplateTupleDesc(int natts);
void GppcTupleDescInitEntry(GppcTupleDesc desc, uint16_t attno,
                            const char *attname, GppcOid typid, int32_t typmod);
在某些情况下,您可能希望从现有元组中的属性定义初始化元组描述符条目。 以下函数获取元组描述符中的属性数,以及描述符中特定属性(按编号)的定义:
int GppcTupleDescNattrs(GppcTupleDesc tupdesc);
const char *GppcTupleDescAttrName(GppcTupleDesc tupdesc, int16_t attno);
GppcOid GppcTupleDescAttrType(GppcTupleDesc tupdesc, int16_t attno);
int16_t GppcTupleDescAttrLen(GppcTupleDesc tupdesc, int16_t attno);
int32_t GppcTupleDescAttrTypmod(GppcTupleDesc tupdesc, int16_t attno);
以下示例初始化两个属性元组描述符。 第一个属性使用来自不同描述符的属性定义进行初始化,第二个属性初始化为布尔类型属性:
GppcTupleDesc       tdesc;
GppcTupleDesc       indesc = some_input_descriptor;

// initialize the tuple descriptor with 2 attributes
tdesc = GppcCreateTemplateTupleDesc(2);

// use third attribute from the input descriptor
GppcTupleDescInitEntry(tdesc, 1, 
	       GppcTupleDescAttrName(indesc, 2),
	       GppcTupleDescAttrType(indesc, 2),
	       GppcTupleDescAttrTypmod(indesc, 2));

// create the boolean attribute
GppcTupleDescInitEntry(tdesc, 2, "is_active", GppcOidBool, 0);

GPPC API定义了一个抽象类型GppcHeapTuple来表示元组/记录/行。 元组由其元组描述符定义,每个元组属性的值以及每个值是否为NULL的指示符。

GPPC API提供了可用于设置和访问元组及其属性的函数:

函数名称 描述
GppcHeapFormTuple() GppcDatum数组中形成一个元组。
GppcBuildHeapTupleDatum() GppcDatum数组中形成一个GppcDatum元组。
GppcGetAttributeByName() 按名称从元组中获取属性。
GppcGetAttributeByNum() 从数字中获取元组的属性(从1开始)。

构建元组GPPC函数的签名是:
GppcHeapTuple GppcHeapFormTuple(GppcTupleDesc tupdesc, GppcDatum *values, bool *nulls);
GppcDatum    GppcBuildHeapTupleDatum(GppcTupleDesc tupdesc, GppcDatum *values, bool *nulls);
以下代码摘录从上面的代码示例中的元组描述符构造GppcDatum元组,并从函数的整数和布尔输入参数构造:
GppcDatum intarg = GPPC_GETARG_INT4(0);
GppcDatum boolarg = GPPC_GETARG_BOOL(1);
GppcDatum result, values[2];
bool nulls[2] = { false, false };

// construct the values array
values[0] = intarg;
values[1] = boolarg;
result = GppcBuildHeapTupleDatum( tdesc, values, nulls );

Set-Returning函数

其签名包括RETURNS SETOF RECORDRETURNS TABLE( ... )的Greenplum数据库UDF是set-returning函数。

GPPC API为GPPC函数返回集(例如,多行/元组)提供支持。 Greenplum数据库为每个行或项目调用一次set-returning函数(SRF)。 该函数必须保存足够的状态以记住它正在做什么并返回每次调用的下一行。 您在SRF上下文中分配的内存必须在多个函数调用中存活。

GPPC API提供宏和函数,以帮助跟踪和设置此上下文,并分配SRF内存。 他们包括:

函数/宏名称 描述
GPPC_SRF_RESULT_DESC() 获取此SRF的输出行元组描述符。结果元组描述符由输出表定义或DESCRIBE函数确定。
GPPC_SRF_IS_FIRSTCALL() 确定这是否是对SRF的第一次调用。
GPPC_SRF_FIRSTCALL_INIT() 初始化SRF上下文。
GPPC_SRF_PERCALL_SETUP() 在每次调用SRF时恢复上下文。
GPPC_SRF_RETURN_NEXT() 从SRF返回值并继续处理。
GPPC_SRF_RETURN_DONE() SRF处理完成的信号。
GppSRFAlloc() 在此SRF上下文中分配内存。
GppSRFAlloc0() 在此SRF上下文中分配内存并将其初始化为零。
GppSRFSave() 在此SRF上下文中保存用户状态。
GppSRFRestore() 在此SRF上下文中还原用户状态。

GppcFuncCallContext结构提供SRF的上下文。 您在第一次调用SRF时创建此上下文。 您的set-returning GPPC函数必须在每次调用时检索函数上下文。 例如:
// set function context
GppcFuncCallContext fctx;
if (GPPC_SRF_IS_FIRSTCALL()) {
    fctx = GPPC_SRF_FIRSTCALL_INIT();
}
fctx = GPPC_SRF_PERCALL_SETUP();
// process the tuple
GPPC函数必须在返回元组结果时提供上下文或指示处理已完成。 例如:
GPPC_SRF_RETURN_NEXT(fctx, result_tuple);
// or
GPPC_SRF_RETURN_DONE(fctx);

使用DESCRIBE函数定义使用RETURNS SETOF RECORD子句的函数的输出元组描述符。 使用GPPC_SRF_RESULT_DESC()宏获取使用RETURNS TABLE( ... )子句的函数的输出元组描述符。

有关set-returning函数代码和部署示例,请参阅GPPC Set-Returning函数示例

表函数

GPPC API提供GppcAnyTable类型以将表作为输入参数传递给函数,或者将表作为函数结果返回。

GPPC API中提供的与表相关的函数和宏包括:

函数/宏名称 描述
GPPC_GETARG_ANYTABLE() 获取任何表函数参数。
GPPC_RETURN_ANYTABLE() 返回表。
GppcAnyTableGetTupleDesc() 获取表的元组描述符。
GppcAnyTableGetNextTuple() 获取表中的下一行。

您可以使用GPPC_GETARG_ANYTABLE()宏来检索表输入参数。 当您有权访问该表时,可以使用GppcAnyTableGetTupleDesc()函数检查该表的元组描述符。 该函数的签名是:
GppcTupleDesc GppcAnyTableGetTupleDesc(GppcAnyTable t);
例如,要检索作为函数的第一个输入参数的表的元组描述符:
GppcAnyTable     intbl;
GppcTupleDesc    in_desc;

intbl = GPPC_GETARG_ANYTABLE(0);
in_desc = GppcAnyTableGetTupleDesc(intbl);
GppcAnyTableGetNextTuple()函数从表中获取下一行。 同样,要从上表中检索下一个元组:
GppcHeapTuple    ntuple;

ntuple = GppcAnyTableGetNextTuple(intbl);

限制

使用Greenplum数据库版本5.0.x的GPPC API不支持以下运算符:

  • integer || integer
  • integer = text
  • text < integer

样例代码

Greenplum数据库github存储库中的gppc test目录包含示例GPPC代码:

  • gppc_demo/ - 示例代码,用于执行GPPC SPI函数,错误报告,数据类型参数和返回宏,set-returning函数和编码函数
  • tabfunc_gppc_demo/ - 示例代码执行GPPC表和set-returning函数

使用PGXS构建GPPC共享库

您可以将使用GPPC API编写的函数编译到Greenplum数据库服务器按需加载的一个或多个共享库中。

您可以使用PostgreSQL构建扩展基础结构(PGXS)根据Greenplum数据库安装为您的GPPC函数构建源代码。 该框架自动化简单模块的通用构建规则。 如果您有一个更复杂的用例,则需要编写自己的构建系统。

要使用PGXS基础结构为使用GPPC API创建的函数生成共享库,请创建一个设置PGXS特定变量的简单Makefile

Note: 有关PGXS支持的Makefile变量的信息, 请参阅PostgreSQL文档中的扩展的构建基础设施
例如,以下Makefile从名为src1.csrc2.c的两个C源文件生成名为sharedlib_name.so的共享库:
MODULE_big = sharedlib_name
OBJS = src1.o src2.o
PG_CPPFLAGS = -I$(shell $(PG_CONFIG) --includedir)
SHLIB_LINK = -L$(shell $(PG_CONFIG) --libdir) -lgppc

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

MODULE_big标识Makefile生成的共享库的基本名称。

PG_CPPFLAGS将Greenplum数据库安装包含目录添加到编译器头文件搜索路径中。

SHLIB_LINK将Greenplum数据库安装库目录添加到链接器搜索路径。 此变量还将GPPC库(-lgppc)添加到link命令。

PG_CONFIGPGXS变量设置和include语句是必需的, 通常位于Makefile的最后三行。

使用Greenplum数据库注册GPPC函数

在用户可以从SQL调用GPPC函数之前,必须使用Greenplum数据库注册该函数。

注册GPPC函数涉及将GPPC函数签名映射到SQL用户定义的函数。 您可以使用CREATE FUNCTION .. AS 命令定义此映射,以指定GPPC共享库名称。 您可以选择为GPPC和SQL函数使用相同的名称或不同的名称。

示例CREATE FUNCTION ... AS 语法如下:
CREATE FUNCTION sql_function_name(arg[, ...]) RETURNS return_type
  AS 'shared_library_path'[, 'gppc_function_name']
LANGUAGE C STRICT [WITH (DESCRIBE=describe_function)];

指定shared_library_path时,可以省略共享库.so扩展名。

如果GPPC函数在名为gppc_try.so的共享库中编译和链接, 则以下命令将本主题前面引用的示例add_int4s()函数注册到名为add_two_int4s_gppc()的SQL UDF:
CREATE FUNCTION add_two_int4s_gppc(int4, int4) RETURNS int8
  AS 'gppc_try.so', 'add_int4s'
LANGUAGE C STRICT;

关于动态加载

您可以在CREATE FUNCTION ... AS命令中指定GPPC共享库的名称, 以在Greenplum数据库的共享库中注册GPPC函数。 Greenplum数据库动态加载程序在用户第一次调用在该共享库中链接的用户定义函数时将GPPC共享库文件加载到内存中。 如果在CREATE FUNCTION ... AS命令中未提供共享库的绝对路径, Greenplum数据库将尝试使用以下有序步骤找到库:

  1. 如果共享库文件路径以字符串$libdir开头, 则Greenplum数据库将在PostgreSQL包库目录中查找该文件。 运行pg_config --pkglibdir命令以确定此目录的位置。
  2. 如果指定了没有目录前缀的共享库文件名, 则Greenplum数据库将在dynamic_library_path服务器配置参数值标识的目录中搜索该文件。
  3. 当前的工作目录。

打包和部署注意事项

您必须以适合Greenplum集群中Greenplum数据库管理员部署的形式打包GPPC共享库和SQL函数注册脚本。 提供GPPC包的特定部署说明。

构建程序包和部署说明时,请考虑以下事项:

  • 考虑提供Greenplum数据库管理员运行的shell脚本或程序,以便将共享库安装到所需的文件系统位置并注册GPPC函数。
  • 必须将GPPC共享库安装到master主机上的相同文件系统位置以及Greenplum数据库群集中的每个segment主机上。
  • gpadmin用户必须具有遍历GPPC共享库文件的完整文件系统路径的权限。
  • 安装在Greenplum数据库部署中后, GPPC共享库的文件系统位置决定了在使用CREATE FUNCTION ... AS命令在库中注册函数时如何引用共享库。
  • 创建一个.sql脚本文件,为GPPC共享库中的每个GPPC函数注册一个SQL UDF。 您在.sql注册脚本中创建的函数必须引用GPPC共享库的部署位置。 在GPPC部署包中包含此脚本。
  • 记录运行GPPC包部署脚本的说明(如果提供)。
  • 如果未在程序包部署脚本中包含此任务,请记录有关安装GPPC共享库的说明。
  • 如果未在程序包部署脚本中包含此任务,请记录有关安装和运行函数注册脚本的说明。

GPPC文本函数示例

在此示例中,您将开发,构建和部署GPPC共享库,并注册并运行名为concat_two_strings的GPPC函数。 此函数使用GPPC API连接两个字符串参数并返回结果。

您将在Greenplum数据库主控主机上开发GPPC函数。 部署您在此示例中创建的GPPC共享库需要对Greenplum数据库集群的管理访问权限。

执行以下过程以运行该示例:

  1. 登录Greenplum数据库主控主机并设置您的环境。例如:
    $ ssh gpadmin@<gpmaster>
    gpadmin@gpmaster$ . /usr/local/greenplum-db/greenplum_path.sh
  2. 创建工作目录并导航到新目录。例如:
    gpadmin@gpmaster$ mkdir gppc_work
    gpadmin@gpmaster$ cd gppc_work
  3. 通过在您选择的编辑器中打开文件,为GPPC源代码准备文件。 例如,要使用vi打开名为gppc_concat.c的文件:
    gpadmin@gpmaster$ vi gppc_concat.c
  4. 将以下代码复制/粘贴到文件中:
    #include <stdio.h>
    #include <string.h>
    #include "gppc.h"
    
    // make the function SQL-invokable
    GPPC_FUNCTION_INFO(concat_two_strings);
    
    // declare the function
    GppcDatum concat_two_strings(GPPC_FUNCTION_ARGS);
    
    GppcDatum
    concat_two_strings(GPPC_FUNCTION_ARGS)
    {
        // retrieve the text input arguments
        GppcText arg0 = GPPC_GETARG_TEXT(0);
        GppcText arg1 = GPPC_GETARG_TEXT(1);
    
        // determine the size of the concatenated string and allocate
        // text memory of this size
        size_t arg0_len = GppcGetTextLength(arg0);
        size_t arg1_len = GppcGetTextLength(arg1);
        GppcText retstring = GppcAllocText(arg0_len + arg1_len);
    
        // construct the concatenated return string
        memcpy(GppcGetTextPointer(retstring), GppcGetTextPointer(arg0), arg0_len);
        memcpy(GppcGetTextPointer(retstring) + arg0_len, GppcGetTextPointer(arg1), arg1_len);
    
        GPPC_RETURN_TEXT( retstring );
    }

    代码声明并实现了concat_two_strings()函数。 它使用GPPC数据类型,宏和函数来获取函数参数, 为连接的字符串分配内存,将参数复制到新字符串中,然后返回结果。

  5. 保存文件并退出编辑器。
  6. 在您选择的编辑器中打开名为Makefile的文件。 将以下文本复制/粘贴到文件中:
    MODULE_big = gppc_concat
    OBJS = gppc_concat.o
    
    PG_CONFIG = pg_config
    PGXS := $(shell $(PG_CONFIG) --pgxs)
    
    PG_CPPFLAGS = -I$(shell $(PG_CONFIG) --includedir)
    SHLIB_LINK = -L$(shell $(PG_CONFIG) --libdir) -lgppc
    include $(PGXS)
  7. 保存文件并退出编辑器。
  8. concat_two_strings()函数构建GPPC共享库。例如:
    gpadmin@gpmaster$ make all

    make命令在当前工作目录中生成名为gppc_concat.so的共享库文件。

  9. 将共享库复制到Greenplum数据库安装。 您必须具有Greenplum数据库管理权限才能复制该文件。 例如:
    gpadmin@gpmaster$ cp gppc_concat.so /usr/local/greenplum-db/lib/postgresql/
  10. 将共享库复制到Greenplum数据库安装中的每个主机。 例如,如果seghostfile包含Greenplum数据库集群中segment主机的列表,每个主机行:
    gpadmin@gpmaster$ gpscp -v -f seghostfile /usr/local/greenplum-db/lib/postgresql/gppc_concat.so =:/usr/local/greenplum-db/lib/postgresql/gppc_concat.so
  11. 打开psql会话。例如:
    gpadmin@gpmaster$ psql -d testdb
  12. 使用Greenplum数据库注册名为concat_two_strings()的GPPC函数。 例如,将Greenplum数据库函数concat_with_gppc()映射到GPPC concat_two_strings()函数:
    testdb=# CREATE FUNCTION concat_with_gppc(text, text) RETURNS text
      AS 'gppc_concat', 'concat_two_strings'
    LANGUAGE C STRICT;
  13. 运行concat_with_gppc()函数。例如:
    testdb=# SELECT concat_with_gppc( 'happy', 'monday' );
     concat_with_gppc
    ------------------
     happymonday
    (1 row)
    

GPPC Set-Returning函数示例

在此示例中,您将开发,构建和部署GPPC共享库。 您还可以为名为return_tbl()的GPPC函数创建并运行.sql注册脚本。 此函数使用GPPC API获取带有整数和文本列的输入表,确定整数列是否大于13, 并返回带有输入整数列的结果表和一个标识整数是否为的整数的布尔列return_tbl()使用GPPC API报告和SRF函数和宏。

您将在Greenplum数据库master主机上开发GPPC函数。 部署您在此示例中创建的GPPC共享库需要对Greenplum数据库集群的管理访问权限。

执行以下过程以运行该示例:

  1. 登录Greenplum数据库master主机并设置您的环境。例如:
    $ ssh gpadmin@<gpmaster>
    gpadmin@gpmaster$ . /usr/local/greenplum-db/greenplum_path.sh
  2. 创建工作目录并导航到新目录。例如:
    gpadmin@gpmaster$ mkdir gppc_work
    gpadmin@gpmaster$ cd gppc_work
  3. 通过在您选择的编辑器中打开文件,为GPPC代码准备源文件。 例如,要使用vi打开名为gppc_concat.c的文件:
    gpadmin@gpmaster$ vi gppc_rettbl.c
  4. 将以下代码复制/粘贴到文件中:
    #include <stdio.h>
    #include <string.h>
    #include "gppc.h"
    
    // initialize the logging level
    GppcReportLevel level = GPPC_INFO;
    
    // make the function SQL-invokable and declare the function
    GPPC_FUNCTION_INFO(return_tbl);
    GppcDatum return_tbl(GPPC_FUNCTION_ARGS);
    
    GppcDatum
    return_tbl(GPPC_FUNCTION_ARGS)
    {
        GppcFuncCallContext	fctx;
        GppcAnyTable	intbl;
        GppcHeapTuple	intuple;
        GppcTupleDesc	in_tupdesc, out_tupdesc;
        GppcBool  		resbool = false;
        GppcDatum  		result, boolres, values[2];
        bool		nulls[2] = {false, false};
    
        // single input argument - the table
        intbl = GPPC_GETARG_ANYTABLE(0);
    
        // set the function context
        if (GPPC_SRF_IS_FIRSTCALL()) {
            fctx = GPPC_SRF_FIRSTCALL_INIT();
        }
        fctx = GPPC_SRF_PERCALL_SETUP();
    
        // get the tuple descriptor for the input table
        in_tupdesc  = GppcAnyTableGetTupleDesc(intbl);
    
        // retrieve the next tuple
        intuple = GppcAnyTableGetNextTuple(intbl);
        if( intuple == NULL ) {
          // no more tuples, conclude
          GPPC_SRF_RETURN_DONE(fctx);
        }
    
        // get the output tuple descriptor and verify that it is
        // defined as we expect
        out_tupdesc = GPPC_SRF_RESULT_DESC();
        if (GppcTupleDescNattrs(out_tupdesc) != 2                ||
            GppcTupleDescAttrType(out_tupdesc, 0) != GppcOidInt4 ||
            GppcTupleDescAttrType(out_tupdesc, 1) != GppcOidBool) {
            GppcReport(GPPC_ERROR, "INVALID out_tupdesc tuple");
        }
    
        // log the attribute names of the output tuple descriptor
        GppcReport(level, "output tuple descriptor attr0 name: %s", GppcTupleDescAttrName(out_tupdesc, 0));
        GppcReport(level, "output tuple descriptor attr1 name: %s", GppcTupleDescAttrName(out_tupdesc, 1));
    
        // retrieve the attribute values by name from the tuple
        bool text_isnull, int_isnull;
        GppcDatum intdat = GppcGetAttributeByName(intuple, "id", &int_isnull);
        GppcDatum textdat = GppcGetAttributeByName(intuple, "msg", &text_isnull);
    
        // convert datum to specific type
        GppcInt4 intarg = GppcDatumGetInt4(intdat);
        GppcReport(level, "id: %d", intarg);
        GppcReport(level, "msg: %s", GppcTextGetCString(GppcDatumGetText(textdat)));
    
        // perform the >13 check on the integer
        if( !int_isnull && (intarg > 13) ) {
            // greater than 13?
            resbool = true;
            GppcReport(level, "id is greater than 13!");
        }
    
        // values are datums; use integer from the tuple and
        // construct the datum for the boolean return
        values[0] = intdat;
        boolres = GppcBoolGetDatum(resbool);
        values[1] = boolres;
    
        // build a datum tuple and return
        result = GppcBuildHeapTupleDatum(out_tupdesc, values, nulls);
        GPPC_SRF_RETURN_NEXT(fctx, result);
    
    }

    代码声明并实现了return_tbl()函数。 它使用GPPC数据类型,宏和函数来获取函数参数,检查元组描述符,构建返回元组,并返回结果。 该函数还使用SRF宏来跟踪函数调用之间的元组上下文。

  5. 保存文件并退出编辑器。
  6. 在您选择的编辑器中打开名为Makefile的文件。 将以下文本复制/粘贴到文件中:
    MODULE_big = gppc_rettbl
    OBJS = gppc_rettbl.o
    
    PG_CONFIG = pg_config
    PGXS := $(shell $(PG_CONFIG) --pgxs)
    
    PG_CPPFLAGS = -I$(shell $(PG_CONFIG) --includedir)
    SHLIB_LINK = -L$(shell $(PG_CONFIG) --libdir) -lgppc
    include $(PGXS)
  7. 保存文件并退出编辑器。
  8. return_tbl()函数构建GPPC共享库。例如:
    gpadmin@gpmaster$ make all

    make命令在当前工作目录中生成名为gppc_rettbl.so的共享库文件。

  9. 将共享库复制到Greenplum数据库安装。 您必须具有Greenplum数据库管理权限才能复制该文件。 例如:
    gpadmin@gpmaster$ cp gppc_rettbl.so /usr/local/greenplum-db/lib/postgresql/

    此命令将共享库复制到$libdir

  10. 将共享库复制到Greenplum数据库安装中的每个主机。 例如,如果seghostfile包含Greenplum数据库集群中segment主机的列表,每个主机一行:
    gpadmin@gpmaster$ gpscp -v -f seghostfile /usr/local/greenplum-db/lib/postgresql/gppc_rettbl.so =:/usr/local/greenplum-db/lib/postgresql/gppc_rettbl.so
  11. 创建.sql文件以注册GPPC return_tbl()函数。 在您选择的编辑器中打开名为gppc_rettbl_reg.sql的文件。
  12. 将以下文本复制/粘贴到文件中:
    CREATE FUNCTION rettbl_gppc(anytable) RETURNS TABLE(id int4, thirteen bool)
      AS 'gppc_rettbl', 'return_tbl'
    LANGUAGE C STRICT;
  13. 通过运行刚刚创建的脚本来注册GPPC功能。 例如,要在名为testdb的数据库中注册该函数:
    gpadmin@gpmaster$ psql -d testdb -f gppc_rettbl_reg.sql
  14. 打开psql会话。例如:
    gpadmin@gpmaster$ psql -d testdb
  15. 创建包含一些测试数据的表。例如:
    CREATE TABLE gppc_testtbl( id int, msg text );
    INSERT INTO gppc_testtbl VALUES (1, 'f1');
    INSERT INTO gppc_testtbl VALUES (7, 'f7');
    INSERT INTO gppc_testtbl VALUES (10, 'f10');
    INSERT INTO gppc_testtbl VALUES (13, 'f13');
    INSERT INTO gppc_testtbl VALUES (15, 'f15');
    INSERT INTO gppc_testtbl VALUES (17, 'f17');
  16. 运行rettbl_gppc()函数。例如:
    testdb=# SELECT * FROM rettbl_gppc(TABLE(SELECT * FROM gppc_testtbl));
     id | thirteen 
    ----+----------
      1 | f
      7 | f
     13 | f
     15 | t
     17 | t
     10 | f
    (6 rows)