GPDB - 内核特性 - 分区表如何处理表名超长

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函数来完成。