GPDB - 内核特性 - 分区表如何处理表名超长
GPDB分区表创建时有可能表名已存在,此时报错退出。但当分区表名超过64字符时,会进行截断,仅保留前63字符,此时就可能出现分区表名不同,截断后创建的分区表名相同从而创建失败的情况;还会出现分区表父表创建成功,但分区子表名创建失败,报表名已存在等错误导致创建失败。本文基于GreenPlum7.0分区表经典语法详细分析分区表名及分区子表名生成机制。
1、分区表名超过64字符
在语法解析阶段将分区表名进行截断,保留前63个字符,并将第64个字符赋予"\0"。该截断功能由函数truncate_identifier完成:
go
void
truncate_identifier(char *ident, int len, bool warn)
{
if (len >= NAMEDATALEN)
{
len = pg_mbcliplen(ident, len, NAMEDATALEN - 1);
if (warn)
{
/*
* We avoid using %.*s here because it can misbehave if the data
* is not valid in what libc thinks is the prevailing encoding.
*/
char buf[NAMEDATALEN];
memcpy(buf, ident, len);
buf[len] = '\0';
ereport(NOTICE,
(errcode(ERRCODE_NAME_TOO_LONG),
errmsg("identifier \"%s\" will be truncated to \"%s\"",
ident, buf)));
}
ident[len] = '\0';
}
}
2、子分区表名的生成
makePartitionCreateStmt函数生成每个层级所有分区创建的CreateStmt链表,当创建分区表时针对分区使用WITH子句指定子分区表名,那么就使用指定的子分区表名创建子分区表,否则调用ChoosePartitionName生成分区表名。
指定子分区表名建表案例如下所示:
sql
CREATE TABLE update_gp_foo (
a_dist int,
b int,
c_part int,
d int
)
WITH (appendonly=false) DISTRIBUTED BY (a_dist) PARTITION BY RANGE(c_part)
(
PARTITION p20190305 START (1) END (2) WITH (tablename='update_gp_foo_1_prt_p20190305', appendonly=false)
);
CREATE TABLE update_gp_foo1 (
a_dist int,
b int,
c_part int,
d int
)
WITH (appendonly=false) DISTRIBUTED BY (a_dist) PARTITION BY RANGE(c_part)
(
PARTITION p20190305 START (1) END (2) WITH (tablename='update_gp_foo1_1_prt_p20190305', appendonly=false)
);
ChoosePartitionName函数中对于指定分区名字的,直接将parentname、level和prt_partname通过函数makeObjectName函数组合起来,即形成子分区表名:parentname_level_prt_partname。当三者超过63字符(最后一个字符需要保存为\0)就会发生截断,其中prt_partname部分不会截断,总是会保留下来;parentname和level两个截断长度较长者,知道三者长度最长不超过63字符。
对于没有指定分区名,则将当前分区层级分区序号(默认分区作为第一个)作为分区名,其中若默认分区指定了名字,而其他分区没有指定,那么其他分区序号从2开始,即prt_2作为分区名。由此parentname、level和prt_partnum通过makeObjectName函数组成的分区表名后,从pg_class中进行查询,若分区表名冲突,则在prt_partnum后追加冲突次数,比如冲突了23次,则形成分区表名为:parentname_level_prt_partnum23
生成分区表名后,通过heap_create_with_catalog创建磁盘文件前,首先根据分区表名在pg_class和pg_type中进行校验,若分区表名已存在则报错退出,否则创建成功后向pg_class中插入一条记录,pg_type中插入2条记录,其typname分别为分区表名和"分区表名",当然若超过63字符则分区表名会截断,若仍旧冲突,则继续在前部加"",比如"__分区表名"。
当然,若分区表主表名超过64字符,则在语法解析阶段就会阶段,通过函数truncate_identifier函数来完成。