PostgreSQLC风格函数TEXT优化
使⽤PostgreSQL C风格函数注意事项
1.在C函数中不检查参数是否为NULL,NULL参数检查在create function sql中设置strict实现.
2.当⽤PG_GETARG_xx获取参数后,根据需要检查参数值是否有效.
3.如不能编译是因为缺少头⽂件,⾃⼰添加缺失的头⽂件即可.
4.PostgreSQL分配和释放内存使⽤palloc,palloc0和pfree.palloc0分配后会将内存设置为0.其它和palloc⼀样,在源码backend/utils/mmgr/mcxt.c中查看.
5.palloc分配的内存⽆需再检查是否分配成功(NULL==ptr),在palloc内部已经检查.
6.内存使⽤原则是谁分配谁释放(函数返回值除外),这点和c/c++⼀致.
7.当palloc分配的内存做为函数的返回值时,不需要pfree释放内存,由PostgreSQL⾃动释放.
常规办法
为更好说明问题,以下步骤⽐较繁琐,
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
PGDLLEXPORT Datum string_test(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(string_test);
Datum string_test(PG_FUNCTION_ARGS) {
int32 val = PG_GETARG_INT32(0);
char *ptmp, *pint;
const char *pcsrc = "汉字测试";
size_t all_size,int_str_size, src_size = strlen(pcsrc);
/*
*  计算拼接字符和整数所需的内存⼤⼩
*  为提升性能int转字符不设置单独的缓冲区
*  直接在分配时多加16个字节存储int转换为字符后的内容
*/
size_t alloc_size = (src_size + 16 + sizeof(0));
char *buffer = (char *)malloc(alloc_size);
if (NULL == buffer)
ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("memory out of range")));
ptmp = buffer;
/*复制字符串*/
memcpy(ptmp, pcsrc, src_size);
ptmp += src_size;
/*将int转换为10进制字符后和字符拼接*/
pint = itoa(val, ptmp, 10);
/*计算int转换为字符后所需的内存⼤⼩*/
int_str_size = strlen(pint);
ptmp += int_str_size;
/*计算实际所需的内存*/
all_size = ptmp - buffer;
buffer[all_size] = '\0';
text *result = cstring_to_text(buffer);
free(buffer);
/*返回拼接后的数据,cstring_to_text⼜复制了⼀次数据,所以内存⼀共分配和复制了两次*/
PG_RETURN_TEXT_P(PointerGetDatum(result));
}
函数cstring_to_text内部直接调⽤了cstring_to_text_with_len(const char *s, int len),cstring_to_text_with_len实现如下:
text* cstring_to_text_with_len(const char *s, int len){
text *result = (text *) palloc(len + VARHDRSZ);
SET_VARSIZE(result, len + VARHDRSZ);
memcpy(VARDATA(result), s, len);
return result;
}
⼀次分配内存完成上述功能
Datum string_test(PG_FUNCTION_ARGS) {
int32 val = PG_GETARG_INT32(0);
char *pbuf,*ptmp, *pint;
const char *pcsrc = "汉字测试";
size_t all_size,int_str_size, src_size = strlen(pcsrc);
/*
*  内存缓冲区
*  计算拼接字符和整数所需的内存⼤⼩
*  为提升性能int转字符不设置单独的缓冲区
*  直接在分配内存时多加16个字节存储int转换为字符后的内容
*/
size_t alloc_size = (src_size + 16 + VARHDRSZ);
/*⼀次性分配内存,返回值也⽤这个函数*/
text *result = (text *)palloc(alloc_size);
/*获取分配的内存空间*/
pbuf = VARDATA(result);
ptmp = pbuf;
/*复制字符串*/
memcpy(ptmp, pcsrc, src_size);
ptmp += src_size;
/*将int转换为10进制字符后和字符拼接*/
pint = itoa(val, ptmp, 10);
/*计算int转换为字符后所需的内存⼤⼩*/
int_str_size = strlen(pint);
ptmp += int_str_size;
/*计算实际所需的内存*/
all_size = ptmp - pbuf;
/*PostgreSQL的字符串不是以NULL结尾的,在开头有4字节说明字符串的长度,类似BSTR的定义,所以不需要字符结束标记*/
//buffer[all_size] = '\0';
/*
*  注意,注意,注意
*  在函数返回前设置实际的内存⼤⼩
*  SET_VARSIZE必须是实际使⽤的内存⼤⼩加上VARHDRSZ,否则PostgreSQL不能正确显⽰⽂本
*  SET_VARSIZE必须是实际使⽤的内存⼤⼩加上VARHDRSZ,否则PostgreSQL不能正确显⽰⽂本
*  SET_VARSIZE必须是实际使⽤的内存⼤⼩加上VARHDRSZ,否则PostgreSQL不能正确显⽰⽂本
*/
SET_VARSIZE(result, all_size + VARHDRSZ);
/*返回拼接后的数据内存分配和复制了⼀次*/
PG_RETURN_TEXT_P(PointerGetDatum(result));
}
⼩结
当使⽤PostgreSQL的text类型时要注意以⼏点:
1.在text类型开头⽤VARHDRSZ字节说明字符串的长度.
截⽌⽬前VARHDRSZ定义为4字节,将来有可能变化 ,所以不要直接使⽤4,应该使⽤VARHDRSZ
2.因此分配内存时,内存⼤⼩必须包含VARHDRSZ.
size_t alloc_size = (src_size + 16 + VARHDRSZ);
src_size + 16是实际的数据存储空间 ,VARHDRSZ字节说明字符串的长度
3.获取分配的数据内存空间使⽤ VARDATA.
pbuf = VARDATA(result);
4.如果明确知道实际的内存⼤⼩,在palloc完成后,就⽴刻调⽤SET_VARSIZE设置内存的⼤⼩
text *result = (text *)palloc(alloc_size);textstyle
SET_VARSIZE(result, all_size + VARHDRSZ);
5.如果在分配时不知道需要的内存⼤⼩,可以在palloc时分配⾜够⼤的内存空间,然后计算实际的内存⼤⼩在返回前调⽤SET_VARSIZE 设置内存的⼤⼩
例如: 28个UTF-8格式的字符要返回,但是在⽣产字符串之间我并不知道28个UTF-8字符实际占⽤的内存⼤⼩,因些在分配前⼀次性分配⾜够的空间
UTF8单个字符最多使⽤6字节
text *result = (text *)palloc(28 * 6  + VARHDRSZ);
现在⽣成28个UTF-8字符,并在⽣成过程中或⽣成完成后计算实际的内存⼤⼩.
现在我们已经知道28个UTF-8字符实际使⽤的内存⼤⼩,因此在函数返回前调⽤SET_VARSIZE设置实际使⽤的内存⼤⼩
SET_VARSIZE(result, all_size + VARHDRSZ);
PG_RETURN_TEXT_P(PointerGetDatum(result));
SET_VARSIZE必须是实际使⽤的内存⼤⼩加上VARHDRSZ,否则PostgreSQL不能正确显⽰⽂本
SET_VARSIZE必须是实际使⽤的内存⼤⼩加上VARHDRSZ,否则PostgreSQL不能正确显⽰⽂本
SET_VARSIZE必须是实际使⽤的内存⼤⼩加上VARHDRSZ,否则PostgreSQL不能正确显⽰⽂本
6.PostgreSQL的字符串不是以NULL结尾的,在开头有4字节说明字符串的长度,类似BSTR的定义,所以不需要字符结束标记NULL
不需要这个
//buffer[all_size] = '\0';
在实践使⽤过程中字符串结束符有没有都可以,如果为了和C函数兼容,可以保留
7.PostgreSQL的TEXT操作函数总结
VARHDRSZ获取TEXT类型头的⼤⼩
在分析内存和设置内存⼤⼩时⼀定要加上VARHDRSZ,主要是palloc,palloc0和SET_VARSIZE.
VARDATA获取分配的数据内存空间

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。