栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Postgresql源码(51)变长类型实现(valena.c)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Postgresql源码(51)变长类型实现(valena.c)

总结(可以先跳过)

varattrib_4b总结:

  • 所有变长类型的判断 都 使用第一个字节:
    • 0x00 (0bxxxx xxx0):is 4B(未压缩数据varattrib_4b,最大1G)
    • 0x00 (0bxxxx xx00):is 4B_U(未压缩数据varattrib_4b,第二个二进制如果没用使用且长度满足,可以把4B转换为1B)
    • 0x00 (0bxxxx xx10):is 4B_C(压缩数据varattrib_4b,最大1G,用于判断是否压缩)
    • 0x00 (0bxxxx xxx1):is 1B(未压缩数据,1B断类型,最大126b)
    • 0x00 (0b0000 0001):is 1B_E(TOAST专用)
  • 4B头共32个二进制位,低2位存控制数据,长度保存在高30位中(注意len永远包含header的4字节,所以能保存的数据是1G-4B)
    • 长度保存:va_header = (uint32) (len) << 2
      • 30个二进制位len范围:0 - 1073741824 B (2^30) == 1024MB
    • 长度读取:(va_header >> 2) & 0x3FFFFFFF
      • 0x3FFFFFFF == 0b0011 1111 1111...右移两位后只需取低30位
  • 1B的头共8个二进制位,低1位存控制数据,长度保存在高7位中(注意len永远包含header的1字节,所以能保存的数据是128B-1B)
    • 长度保存:va_header = (uint8) (len) << 1) | 0x01
      • 7个二进制len范围:0 - 128 B (2^7) ,控制位顺便加个1表示当前是varattrib_1b
    • 长度读取:(va_header >> 1) & 0x7F
      • 0x7F == 0b0111 1111 右移一位后只需取低7位
  • 4B到1B的转换
    • 4B的头是4字节,1B的头是1字节,转换也提供了几个宏,简单的说就是判断数据len能不能被1B装得下:VARATT_CAN_MAKE_SHORT

text支持大量字符换操作函数,匹配C++标准库,字符串操作函数一般都不需要自己写,用时先查询varlena.c

text

postgresql中经常可以看到text类型,直接调试时可以看到这样的结果:

(gdb) p *t
$6 = {vl_len_ = "<0000", vl_dat = 0x1bd49a4 "hello world~20"}

原因是text的类型的定义,数据是用char表示的,所以打印出来默认都是字符类型。这里char并不是字节的含义,头部的四个字节是按二进制位拆分使用的。

struct varlena
{
	char		vl_len_[4];		
	char		vl_dat[FLEXIBLE_ARRAY_MEMBER];	
};

#define VARHDRSZ		((int32) sizeof(int32))
typedef struct varlena text;
text真身

变长字符较常用的是保存在下面几类结构之中

(其实还有其他很多数据结构,形如header+data的格式例如ArrayType,header是固定的格式4B,data随意)

typedef union
{
	struct						
	{
		uint32		va_header;
		char		va_data[FLEXIBLE_ARRAY_MEMBER];
	}			va_4byte;
	struct						
	{
		uint32		va_header;
		uint32		va_tcinfo;	
		char		va_data[FLEXIBLE_ARRAY_MEMBER]; 
	}			va_compressed;
} varattrib_4b;

typedef struct
{
	uint8		va_header;
	char		va_data[FLEXIBLE_ARRAY_MEMBER]; 
} varattrib_1b;


typedef struct
{
	uint8		va_header;		
	uint8		va_tag;			
	char		va_data[FLEXIBLE_ARRAY_MEMBER]; 
} varattrib_1b_e;

具体存放时会按用途选一个结构,其中varattrib_4b是可以用varlena来表示的,另外两种一般用char*直接使用

varattrib_4b  ---使用--->  varlena、bytea(varlena)、text(varlena)、BpChar(varlena)、VarChar(varlena)
varattrib_1b  ---使用--->  Pointer(char*)、Datum(uintptr_t)、char*
varattrib_1b_e

设计思路比较简单,都是结构体头部放一个header按位存一些控制信息,后面跟数据。

PG提供了一系列宏来可以很便捷的操作这些变长类型,而且如果你的自定义结构体也符合header+data的结构,也可以直接使用这些宏操作你的结构体。

例如ArrayType也是4字节header,后面放数据。就可以使用SET_VARSIZE宏把数据长度放入header中记录了。后面也可以用其他宏把长度取出。

typedef struct ArrayType
{
	int32		vl_len_;		
	int			ndim;			
	int32		dataoffset;		
	Oid			elemtype;		
} ArrayType;

// 使用
ArrayType  *aresult = (ArrayType *) result;
...
SET_VARSIZE(aresult, allocated_size);

这里面最常用的还是4B头的varattrib_4b,其他类型使用大同小异,后面重点分析varattrib_4b的原理和使用方法。

varattrib_4b

varattrib_4b用union提供了两套用法:存一般数据;存压缩数据。

typedef union
{
	struct						
	{
		uint32		va_header;
		char		va_data[FLEXIBLE_ARRAY_MEMBER];
	}			va_4byte;
	struct						
	{
		uint32		va_header;
		uint32		va_tcinfo;	
		char		va_data[FLEXIBLE_ARRAY_MEMBER]; 
	}			va_compressed;
} varattrib_4b;

PG默认提供了两套宏,分别是大端序和小端序。Linux默认使用小端序:


// 判断类型
#define VARATT_IS_4B(PTR) 
	((((varattrib_1b *) (PTR))->va_header & 0x01) == 0x00)
#define VARATT_IS_4B_U(PTR) 
	((((varattrib_1b *) (PTR))->va_header & 0x03) == 0x00)
#define VARATT_IS_4B_C(PTR) 
	((((varattrib_1b *) (PTR))->va_header & 0x03) == 0x02)
#define VARATT_IS_1B(PTR) 
	((((varattrib_1b *) (PTR))->va_header & 0x01) == 0x01)
#define VARATT_IS_1B_E(PTR) 
	((((varattrib_1b *) (PTR))->va_header) == 0x01)

// 长度操作
#define VARSIZE_4B(PTR) 
	((((varattrib_4b *) (PTR))->va_4byte.va_header >> 2) & 0x3FFFFFFF)

#define SET_VARSIZE_4B(PTR,len) 
	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2))
#define SET_VARSIZE_4B_C(PTR,len) 
	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2) | 0x02)

// 获取数据
#define VARDATA_4B(PTR)		(((varattrib_4b *) (PTR))->va_4byte.va_data)
#define VARDATA_4B_C(PTR)	(((varattrib_4b *) (PTR))->va_compressed.va_data)

// 压缩用
#define VARHDRSZ_COMPRESSED		offsetof(varattrib_4b, va_compressed.va_data)
#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) 
	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK)
#define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(PTR) 
	(((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS)

varattrib_4b总结:

  • 所有变长类型的判断 都 使用第一个字节:
    • 0x00 (0bxxxx xxx0):is 4B(未压缩数据varattrib_4b,最大1G)
    • 0x00 (0bxxxx xx00):is 4B_U(未压缩数据varattrib_4b,第二个二进制如果没用使用且长度满足,可以把4B转换为1B)
    • 0x00 (0bxxxx xx10):is 4B_C(压缩数据varattrib_4b,最大1G,用于判断是否压缩)
    • 0x00 (0bxxxx xxx1):is 1B(未压缩数据,1B断类型,最大126b)
    • 0x00 (0b0000 0001):is 1B_E(TOAST专用)
  • 4B头共32个二进制位,低2位存控制数据,长度保存在高30位中(注意len永远包含header的4字节,所以能保存的数据是1G-4B)
    • 长度保存:va_header = (uint32) (len) << 2
      • 30个二进制位len范围:0 - 1073741824 B (2^30) == 1024MB
    • 长度读取:(va_header >> 2) & 0x3FFFFFFF
      • 0x3FFFFFFF == 0b0011 1111 1111...右移两位后只需取低30位
  • 1B的头共8个二进制位,低1位存控制数据,长度保存在高7位中(注意len永远包含header的1字节,所以能保存的数据是128B-1B)
    • 长度保存:va_header = (uint8) (len) << 1) | 0x01
      • 7个二进制len范围:0 - 128 B (2^7) ,控制位顺便加个1表示当前是varattrib_1b
    • 长度读取:(va_header >> 1) & 0x7F
      • 0x7F == 0b0111 1111 右移一位后只需取低7位
  • 4B到1B的转换
    • 4B的头是4字节,1B的头是1字节,转换也提供了几个宏,简单的说就是判断数据len能不能被1B装得下:VARATT_CAN_MAKE_SHORT
varlena.c函数

text支持大量字符换操作函数,匹配C++标准库,这里记录一部分便于查询使用

1 text和cstring的相互转换
  • cstring转换到text:默认用4B
  • text转cstring:不知道text是哪种类型,需要先判断,在转换。VARSIZE_ANY_EXHDR宏内包含了控制位的判断逻辑。
  • textin:调用cstring_to_text
  • textout:调用text_to_cstring
text *
cstring_to_text(const char *s)
{
	return cstring_to_text_with_len(s, strlen(s));
}

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;
}

char *
text_to_cstring(const text *t)
{
	
	text	   *tunpacked = pg_detoast_datum_packed(unconstify(text *, t));
	int			len = VARSIZE_ANY_EXHDR(tunpacked);
	char	   *result;

	result = (char *) palloc(len + 1);
	memcpy(result, VARDATA_ANY(tunpacked), len);
	result[len] = '';

	if (tunpacked != t)
		pfree(tunpacked);

	return result;
}


Datum
textin(PG_FUNCTION_ARGS)
{
	char	   *inputText = PG_GETARG_CSTRING(0);

	PG_RETURN_TEXT_P(cstring_to_text(inputText));
}


Datum
textout(PG_FUNCTION_ARGS)
{
	Datum		txt = PG_GETARG_DATUM(0);

	PG_RETURN_CSTRING(TextDatumGetCString(txt));
}
2 textcat追加
  • 算好了两个长度和,新建一个text,SET_VARSIZE
  • 数据拷贝到新建text中
Datum
textcat(PG_FUNCTION_ARGS)
{
	text	   *t1 = PG_GETARG_TEXT_PP(0);
	text	   *t2 = PG_GETARG_TEXT_PP(1);

	PG_RETURN_TEXT_P(text_catenate(t1, t2));
}

static text *
text_catenate(text *t1, text *t2)
{
	text	   *result;
	int			len1,
				len2,
				len;
	char	   *ptr;

	len1 = VARSIZE_ANY_EXHDR(t1);
	len2 = VARSIZE_ANY_EXHDR(t2);

	
	if (len1 < 0)
		len1 = 0;
	if (len2 < 0)
		len2 = 0;

	len = len1 + len2 + VARHDRSZ;
	result = (text *) palloc(len);

	
	SET_VARSIZE(result, len);

	
	ptr = VARDATA(result);
	if (len1 > 0)
		memcpy(ptr, VARDATA_ANY(t1), len1);
	if (len2 > 0)
		memcpy(ptr + len1, VARDATA_ANY(t2), len2);

	return result;
}
3 text_substring子串
static text *
text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
4 text_substring重叠子串替换
static text *
text_overlay(text *t1, text *t2, int sp, int sl)
5 text_position子串位置
static int
text_position(text *t1, text *t2, Oid collid)
6 varstr_cmp字典序比较
int
varstr_cmp(const char *arg1, int len1, const char *arg2, int len2, Oid collid)
  
// texteq text_lt text_le text_gt text_ge
// 
7 text_starts_with
Datum
text_starts_with(PG_FUNCTION_ARGS)
8 太多了详见varlena.c
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/933959.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号