看起来postgres upper/lower
函数不处理土耳其语字符集中的选择字符。
select upper('Aaı'), lower('Aaİ') from mytable;
返回:
AAı, aaİ
而不是:
AAI, aai
请注意,正常的英文字符可以正确转换,但不能转换为土耳其语I(较低或较高)
Postgres版本:9.2 32 bit
数据库编码(其中任何一个都是相同的结果):UTF-8, WIN1254, C
客户端编码:
UTF-8, WIN1254, C
操作系统:Windows 7 enterprise edition 64bit
SQL函数lower
和upper
在UTF-8编码数据库上为ı和İ返回以下相同的字节
\xc4b1
\xc4b0
以下是WIN1254(土耳其语)编码数据库
\xfd
\xdd
我希望我的调查是错误的,而且我错过了一些东西。
答案 0 :(得分:9)
您的问题 100%Windows。(或者更确切地说,使用PostgreSQL构建的Microsoft Visual Studio更准确。)
对于记录,SQL UPPER
最终通过几乎所有正确的参数调用Windows LCMapStringW
(通过towupper
通过str_toupper
) (locale 1055土耳其语为UTF-8
- 编码的,Turkish_Turkey
数据库),
<强>但强>
Visual Studio运行时(towupper
)未在LCMapStringW
的 dwMapFlags 中设置LCMAP_LINGUISTIC_CASING
位。 (我可以确认设置它可以解决问题。)这不是Microsoft的错误;它是设计的,可能永远不会被“修复”(哦遗产的乐趣。)
你有三种方法:
MSVCR100.DLL
目录中放入修补的32位bin
(但UPPER
和LOWER
可行,其他内容例如整理可能会继续失败 - 再次,在Windows级别.YMMV。) 为了完整(和怀旧的乐趣) ONLY ,以下是修补Windows系统的过程(但请记住,除非您将从摇篮中管理此PostgreSQL实例严重的,你可能会给你的继任者带来很大的麻烦;无论何时从头开始部署一个新的测试或备份系统,你或你的继任者都必须记得再次申请补丁 - 如果让我们说你一个一天升级到PostgreSQL 10,即使用MSVCR120.DLL
代替MSVCR100.DLL
,那么你也必须尝试修补新的DLL。)在测试系统上
C:\WINDOWS\SYSTEM32\MSVCR100.DLL
bin
目录下使用相同名称立即保存DLL(不要尝试使用资源管理器或命令行复制文件,它们可能会复制64位版本)4E 14 33 DB 3B CB 0F 84 41 12 00 00 B8 00 01 00 00
4E 14 33 DB 3B CB 0F 84 41 12 00 00 B8 00 01 00 01
FC 51 6A 01 8D 4D 08 51 68 00 02 00 00 50 E8 E2
FC 51 6A 01 8D 4D 08 51 68 00 02 00 01 50 E8 E2
bin
目录下重新保存,然后重新启动PostgreSQL并重新运行查询。
Turkish_Turkey
LC_CTYPE
进行UTF-8编码,LC_COLLATE
和postgres.exe
MSVCR100.DLL
32-bit Dependency Walker并确保它指示它从PostgreSQL bin
目录中加载bin
。citext
目录并重新启动。但请记住,当您将数据从Ubuntu系统移出或从修补的Windows系统移动到未修补的Windows系统时,您将再次遇到问题,如果Windows实例,您可能无法在Ubuntu上导回此数据在UPPER
字段或基于LOWER
/ {{1}}的函数索引中引入了重复项。
答案 1 :(得分:3)
似乎给我您的问题与Windows有关。这就是Ubuntu(Postgres 8.4.14),数据库编码UTF-8:
的样子test=# select upper('Aaı'), lower('Aaİ');
upper | lower
-------+-------
AAI | aai
(1 row)
我的建议是 - 如果你必须使用Windows - 编写一个存储过程,为你做转换。使用内置replace
:replace('abcdefabcdef', 'cd', 'XX')
返回abXXefabXXef
。可能有一个更优化的解决方案,我并不认为这种方法是正确的。
答案 2 :(得分:1)
上述问题的根源。似乎问题只发生在“I”到“ı”和“i”到“İ”的转换中。作为一种解决方法,只需在调用下层或上层函数之前直接替换这些字符,如下所示:
SELECT lower(replace('IİĞ', 'I', 'ı')) -> ıiğ
SELECT upper(replace('ıiğ', 'i', 'İ')) -> IİĞ
答案 3 :(得分:0)
这确实是PostgreSQL中的错误(即使在当前的git树中仍然没有修复)。 证明:https://github.com/postgres/postgres/blob/master/src/port/pgstrcasecmp.c
PostgreSQL开发人员甚至特别提到那里的土耳其语字符:
此文件中实现的SQL99指定了Unicode感知的大小写规范化,我们还没有 拥有基础设施。相反,我们使用tolower()来提供 区域设置感知翻译。 但是,有一些区域设置也不对(例如,土耳其语可能会对'我'和'我'做一些奇怪的事情。) 我们目前的妥协是对字符使用tolower() 高位设置,并使用仅ASCII的7位下行 字符。
pg_upper()
非常简单(作为其随附pg_tolower()
):
unsigned char
pg_toupper(unsigned char ch)
{
if (ch >= 'a' && ch <= 'z')
ch += 'A' - 'a';
else if (IS_HIGHBIT_SET(ch) && islower(ch))
ch = toupper(ch);
return ch;
}
正如您所看到的,此代码不会将其参数视为Unicode代码点,并且不能正确地100%正常工作,除非当前选择的区域设置恰好是我们关心的区域(如土耳其非unicode区域设置)和操作系统提供的非unicode toupper()
正常工作。
这真的很难过,我只希望这将在即将发布的PostgreSQL版本中得到解决......