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函数。
本主题包含以下信息:
使用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函数中使用这些类型。
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专门处理文本,数字和时间戳数据类型,提供对这些类型进行操作的函数。
GppcText message; GppcInt4 arg1; GppcNumeric total_sales;
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_FUNCTION_INFO(add_int4s); GppcDatum add_int4s(GPPC_FUNCTION_ARGS); GppcDatum add_int4s(GPPC_FUNCTION_ARGS) { // code here }
GppcInt4 first_int = GPPC_GETARG_INT4(0); GppcInt4 second_int = GPPC_GETARG_INT4(1);
GppcInt8 sum = first_int + second_int; GPPC_RETURN_INT8(sum);
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 *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);
错误报告和记录
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() | 如果中断挂起则出错。 |
void GppcReport(GppcReportLevel elevel, const char *fmt, ...);
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。 |
GppcSPIConnect(); GppcSPIExec(...) // process the results - GppcSPIGetValue(...), GppcSPIGetDatum(...) GppcSPIFinish()
GppcSPIResult GppcSPIExec(const char *sql_statement, long rcount);
typedef struct GppcSPIResultData { struct GppcSPITupleTableData *tuptable; uint32_t processed; uint32_t current; int rescode; } GppcSPIResultData; typedef GppcSPIResultData *GppcSPIResult;
您可以设置和使用GppcSPIResult结构中的current字段来检查tuptable结果数据的每一行。
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开始)。 |
GppcHeapTuple GppcHeapFormTuple(GppcTupleDesc tupdesc, GppcDatum *values, bool *nulls); GppcDatum GppcBuildHeapTupleDatum(GppcTupleDesc tupdesc, GppcDatum *values, bool *nulls);
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 RECORD或RETURNS 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上下文中还原用户状态。 |
// set function context GppcFuncCallContext fctx; if (GPPC_SRF_IS_FIRSTCALL()) { fctx = GPPC_SRF_FIRSTCALL_INIT(); } fctx = GPPC_SRF_PERCALL_SETUP(); // process the tuple
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() | 获取表中的下一行。 |
GppcTupleDesc GppcAnyTableGetTupleDesc(GppcAnyTable t);
GppcAnyTable intbl; GppcTupleDesc in_desc; intbl = GPPC_GETARG_ANYTABLE(0); in_desc = GppcAnyTableGetTupleDesc(intbl);
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。
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_CONFIG和PGXS变量设置和include语句是必需的, 通常位于Makefile的最后三行。
使用Greenplum数据库注册GPPC函数
在用户可以从SQL调用GPPC函数之前,必须使用Greenplum数据库注册该函数。
注册GPPC函数涉及将GPPC函数签名映射到SQL用户定义的函数。 您可以使用CREATE FUNCTION .. AS 命令定义此映射,以指定GPPC共享库名称。 您可以选择为GPPC和SQL函数使用相同的名称或不同的名称。
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扩展名。
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数据库将尝试使用以下有序步骤找到库:
- 如果共享库文件路径以字符串$libdir开头, 则Greenplum数据库将在PostgreSQL包库目录中查找该文件。 运行pg_config --pkglibdir命令以确定此目录的位置。
- 如果指定了没有目录前缀的共享库文件名, 则Greenplum数据库将在dynamic_library_path服务器配置参数值标识的目录中搜索该文件。
- 当前的工作目录。
打包和部署注意事项
您必须以适合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数据库集群的管理访问权限。
执行以下过程以运行该示例:
- 登录Greenplum数据库主控主机并设置您的环境。例如:
$ ssh gpadmin@<gpmaster> gpadmin@gpmaster$ . /usr/local/greenplum-db/greenplum_path.sh
- 创建工作目录并导航到新目录。例如:
gpadmin@gpmaster$ mkdir gppc_work gpadmin@gpmaster$ cd gppc_work
- 通过在您选择的编辑器中打开文件,为GPPC源代码准备文件。
例如,要使用vi打开名为gppc_concat.c的文件:
gpadmin@gpmaster$ vi gppc_concat.c
- 将以下代码复制/粘贴到文件中:
#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数据类型,宏和函数来获取函数参数, 为连接的字符串分配内存,将参数复制到新字符串中,然后返回结果。
- 保存文件并退出编辑器。
- 在您选择的编辑器中打开名为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)
- 保存文件并退出编辑器。
- 为concat_two_strings()函数构建GPPC共享库。例如:
gpadmin@gpmaster$ make all
make命令在当前工作目录中生成名为gppc_concat.so的共享库文件。
- 将共享库复制到Greenplum数据库安装。
您必须具有Greenplum数据库管理权限才能复制该文件。
例如:
gpadmin@gpmaster$ cp gppc_concat.so /usr/local/greenplum-db/lib/postgresql/
- 将共享库复制到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
- 打开psql会话。例如:
gpadmin@gpmaster$ psql -d testdb
- 使用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;
- 运行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数据库集群的管理访问权限。
执行以下过程以运行该示例:
- 登录Greenplum数据库master主机并设置您的环境。例如:
$ ssh gpadmin@<gpmaster> gpadmin@gpmaster$ . /usr/local/greenplum-db/greenplum_path.sh
- 创建工作目录并导航到新目录。例如:
gpadmin@gpmaster$ mkdir gppc_work gpadmin@gpmaster$ cd gppc_work
- 通过在您选择的编辑器中打开文件,为GPPC代码准备源文件。
例如,要使用vi打开名为gppc_concat.c的文件:
gpadmin@gpmaster$ vi gppc_rettbl.c
- 将以下代码复制/粘贴到文件中:
#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宏来跟踪函数调用之间的元组上下文。
- 保存文件并退出编辑器。
- 在您选择的编辑器中打开名为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)
- 保存文件并退出编辑器。
- 为return_tbl()函数构建GPPC共享库。例如:
gpadmin@gpmaster$ make all
make命令在当前工作目录中生成名为gppc_rettbl.so的共享库文件。
- 将共享库复制到Greenplum数据库安装。
您必须具有Greenplum数据库管理权限才能复制该文件。
例如:
gpadmin@gpmaster$ cp gppc_rettbl.so /usr/local/greenplum-db/lib/postgresql/
此命令将共享库复制到$libdir
- 将共享库复制到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
- 创建.sql文件以注册GPPC return_tbl()函数。 在您选择的编辑器中打开名为gppc_rettbl_reg.sql的文件。
- 将以下文本复制/粘贴到文件中:
CREATE FUNCTION rettbl_gppc(anytable) RETURNS TABLE(id int4, thirteen bool) AS 'gppc_rettbl', 'return_tbl' LANGUAGE C STRICT;
- 通过运行刚刚创建的脚本来注册GPPC功能。
例如,要在名为testdb的数据库中注册该函数:
gpadmin@gpmaster$ psql -d testdb -f gppc_rettbl_reg.sql
- 打开psql会话。例如:
gpadmin@gpmaster$ psql -d testdb
- 创建包含一些测试数据的表。例如:
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');
- 运行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)