关于PostgreSQL的LC_CTYPE

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: LC_CTYPE代表了区域中的字符分类,比如哪些字符是字母,哪些是数字,大小写等。PostgreSQL支持区域相关的行为,其底层实现是调用了操作系统提供的相关接口,比如判断字符大小写的isupper()。
LC_CTYPE代表了区域中的字符分类,比如哪些字符是字母,哪些是数字,大小写等。PostgreSQL支持区域相关的行为,其底层实现是调用了操作系统提供的相关接口,比如判断字符大小写的isupper()。因此PostgreSQL中字符分类相关的行为和OS一致,但是实测发现,还是有一些差别的。

根据PostgreSQL的手册,受字符分类(LC_CTYPE)影响的几个功能,有以下几个。
  • upper, lower, initcap
  • 大小写不敏感的模式匹配
  • 使用了字符分类的正则表达式匹配

下面做一些测试。测试环境为CentOS 6.5 + PostgreSQL 9.3。
PostgreSQL的 LC_CTYPE值可以在initdb或createdb时指定,也可以通过collate(实际是LC_COLLATE+LC_CTYPE的组合)在建表或SQL的表达式中指定。下面的测试,使用表达式指定LC_CTYPE。

LC_CTYPE为C时,不能识别全角英文字母。

  1. postgres=# select upper('a' collate "C" );
  2.  a

  3. postgres=# select lower('A' collate "C" );
  4.  A

  5. postgres=# select initcap('aaa' collate "C" );
  6.  aaa

  7. postgres=# select 'a' ilike 'A' collate "C";
  8.  f

LC_CTYPE为zh_CN时,可以识别全角英文字母 。

  1. postgres=# select upper('a' collate "zh_CN");
  2.  A

  3. postgres=# select lower('A' collate "zh_CN");
  4.  a

  5. postgres=# select initcap('aaa' collate "zh_CN");
  6.  Aaa

  7. postgres=# select 'a' ilike 'A' collate "zh_CN";
  8.  t

然而对正则表达式的字符分类,不论区域是什么都不识别全角英文字母。

  1. postgres=# select 'A' collate "C" ~ '[[:upper:]]';
  2.  f

  3. postgres=# select '1' collate "C" ~ '[[:alnum:]]';
  4.  f

  5. postgres=# select 'A' collate "zh_CN" ~ '[[:upper:]]';
  6.  f

  7. postgres=# select '1' collate "zh_CN" ~ '[[:alnum:]]';
  8.  f

但是OS是支持的。

  1. [chenhj@hanode1 ~]$ export LC_ALL=C
  2. [chenhj@hanode1 ~]$ echo 'A' |grep '[[:upper:]]';
  3. [chenhj@hanode1 ~]$ echo '1' |grep '[[:alnum:]]';
  4. [chenhj@hanode1 ~]$ export LC_ALL=zh_CN.utf8
  5. [chenhj@hanode1 ~]$ echo 'A' |grep '[[:upper:]]';

  6. [chenhj@hanode1 ~]$ echo '1' |grep '[[:alnum:]]';

为什么会这样?
查看了PostgreSQL中正则表达式实现的代码。原来PostgreSQL中为了确保性能预先把字符分类属性都计算好了缓存起来的。而缓存的字符有限,最多也就缓存pg_wchar值(UTF编码时 pg_wchar值相当于unicode代码点)是0~0x7FF的字符,其他的字符都认为不匹配。

src/backend/regex/regc_pg_locale.c

  1. pg_ctype_get_cache(pg_wc_probefunc probefunc)
  2. {
  3.         case PG_REGEX_LOCALE_WIDE:
  4.         case PG_REGEX_LOCALE_WIDE_L:
  5.             max_chr = (pg_wchar) 0x7FF;
  6. ...
  7.     for (cur_chr = 0; cur_chr = max_chr; cur_chr++)
  8.     {
  9.         if ((*probefunc) (cur_chr))
  10.             nmatches++;
  11.         else if (nmatches > 0)
  12.         {
  13.             if (!store_match(pcc, cur_chr - nmatches, nmatches))
  14.                 goto out_of_memory;
  15.             nmatches = 0;
  16.         }
  17.     }
  18. ...
  19. }

src/backend/utils/mb/wchar.c
  1. /*
  2.  * convert UTF8 string to pg_wchar (UCS-4)
  3.  * caller must allocate enough space for "to", including a trailing
  4.  * len: length of from.
  5.  * "from" not necessarily null terminated.
  6.  */
  7. static int
  8. pg_utf2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
  9. {
  10.     int            cnt = 0;
  11.     uint32        c1,
  12.                 c2,
  13.                 c3,
  14.                 c4;

  15.     while (len > 0 && *from)
  16.     {
  17.         if ((*from & 0x80) == 0)
  18.         {
  19.             *to = *from++;
  20.             len--;
  21.         }
  22.         else if ((*from & 0xe0) == 0xc0)
  23.         {
  24.             if (len 2)
  25.                 break;            /* drop trailing incomplete char */
  26.             c1 = *from++ & 0x1f;
  27.             c2 = *from++ & 0x3f;
  28.             *to = (c1 6) | c2;
  29.             len -= 2;
  30.         }
  31.         else if ((*from & 0xf0) == 0xe0)
  32.         {
  33.             if (len 3)
  34.                 break;            /* drop trailing incomplete char */
  35.             c1 = *from++ & 0x0f;
  36.             c2 = *from++ & 0x3f;
  37.             c3 = *from++ & 0x3f;
  38.             *to = (c1 12) | (c2 6) | c3;
  39.             len -= 3;
  40.         }
  41.         else if ((*from & 0xf8) == 0xf0)
  42.         {
  43.             if (len 4)
  44.                 break;            /* drop trailing incomplete char */
  45.             c1 = *from++ & 0x07;
  46.             c2 = *from++ & 0x3f;
  47.             c3 = *from++ & 0x3f;
  48.             c4 = *from++ & 0x3f;
  49.             *to = (c1 18) | (c2 12) | (c3 6) | c4;
  50.             len -= 4;
  51.         }
  52.         else
  53.         {
  54.             /* treat a bogus char as length 1; not ours to raise error */
  55.             *to = *from++;
  56.             len--;
  57.         }
  58.         to++;
  59.         cnt++;
  60.     }
  61.     *to = 0;
  62.     return cnt;
  63. }


而全角英文的unicode代码点是超过0x7FF的。
/usr/share/i18n/locales/i18n

  1. ...
  2. upper /
  3. ...
  4. % HALFWIDTH AND FULLWIDTH FORMS/
  5.    UFF21>..UFF3A>

再试一下0x7FF以内的某个字符,发现 确实是支持的。
比如下面的 ' ? '(0xc5)


话说这些看上去很古怪的字符,会有人在中文里用吗?




相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
存储 安全 索引
vacuum freeze无法回收事务号问题分析
vacuum freeze报错问题分析
4687 0
|
安全 关系型数据库 数据库
Postgresql 数据库用户权限授权(用户角色分配模式)
为了更方面和安全地管理数据库用户账号权限安全,实现通过用户角色代理的模式,实现用户账号功能授权的模式
19430 2
Postgresql 数据库用户权限授权(用户角色分配模式)
|
12月前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
16521 31
|
SQL 关系型数据库 数据库
手把手教你管理PostgreSQL数据库及其对象
手把手教你管理PostgreSQL数据库及其对象
642 0
|
SQL 数据库
`UPDATE FROM` 的语法以及常见的更新操作方式
`UPDATE FROM` 的语法以及常见的更新操作方式
2651 2
|
弹性计算 运维 监控
多云基础设施的统一纳管与运维实践分享
阿里云弹性计算团队十三位产品专家和技术专家共同分享云上运维深度实践,详细阐述如何利用CloudOps工具实现运维提效、弹性降本。
813 1
|
Unix Linux Shell
在Linux中提示No such file or directory解决方法
在Linux中提示No such file or directory解决方法
1313 0
|
存储 安全 Java
网站安全测试,会话 cookie 中缺少 HttpOnly 属性
可能会窃取或操纵客户会话和 cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务 “HttpOnly”属性的会话 cookie。由于此会话 cookie 不包含“HttpOnly”属性,因此 注入站点的恶意脚本可能访问此 cookie,并窃取它的值。任何存储在会话令牌中的 信息都可能被窃取,并在稍后用于身份盗窃或用户伪装。
951 0
|
存储 Oracle 关系型数据库
|
Unix Linux Android开发
1.4 类UNIX系统是什么鬼?
上节《UNIX和linux的区别》中讲到了 UNIX 系统的历史,UNIX 是操作系统的开山鼻祖,是操作系统的发源地,后来的 Windows 和 Linux 都参考了 UNIX。
1096 0
1.4 类UNIX系统是什么鬼?