2015-03-01 18 views
6

Aby napisać kod do sprawdzania spójności danych przechowywanych zarówno w serwerze SQL, jak iw PostgreSQL, planuję obliczyć MD5 na danych tabeli dla obu baz danych i sprawdzić, czy są one równy. Działa to dobrze, tak długo, jak dane jest zwykły tekst (ANSI), jak poniżej:Niezgodność wartości MD5 między serwerem SQL a PostgreSQL

sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', 'a')); 
0x0cc175b9c0f1b6a831c399e269772661 


postgres=# select MD5('a'); 
0cc175b9c0f1b6a831c399e269772661 

Teraz, gdy próbuję użyć niektórych Hangul (koreański) znaków, nie mecz MD5:

sql-server> SELECT master.dbo.fn_varbintohexstr(HashBytes('MD5', '무')); 
0x7827b52f65d9f7777d37071cbbbf7f2d 


postgres=# select MD5('무'); 
cb3e9be1a3a28b355eabae1fa1e291b3 

Jak według mojego rozumowania, powodem niedopasowania jest to, że znaki Unicode są przechowywane jako kodowanie UCS-2 (poprawione 16-bitowe kodowanie) w serwerze SQL i kodowanie UTF-8 w PostgreSQL. A ponieważ MD5 działa na bitach znaków, sekwencja bitów znaków byłaby inna zarówno w serwerze SQL, jak iw PostgreSQL.

jak mam do czynienia głównie z Hangul zestawu znaków, obejście użyłem w PostgreSQL jest konwersja kodowania z UTF-8 do UHC (Universal hangyl zestawu znaków) przed obliczeniem hash jak poniżej:

postgres=# select MD5(CONVERT('무'::bytea,'UTF8','UHC')); 
7827b52f65d9f7777d37071cbbbf7f2d 

Jak widać powyższa wartość skrótu jest taka sama jak dla serwera SQL.

Wszystko jest w porządku, o ile mam do czynienia ze znakami Hangul. Ale niektóre tabele zawiera mieszankę Hangul i chińskich znaków i konwersja nie powiedzie się w tym przypadku:

postgres=# select MD5(CONVERT('무么'::bytea,'UTF8','UHC')); 
ERROR: character 0xe4b988 of encoding "UTF8" has no equivalent in "UHC" 
postgres=# 

Błąd sens jak nie istnieją równoważne z chińskich znaków w UHC zestawu znaków.

Jak mogę to sprawić? Zasadniczo, muszę znaleźć sposób na konwersję UCS-2 na UTF-8 na serwerze SQL lub na konwersję UTF-8 na UCS-2 w PostgreSQL przed obliczeniem MD5. Chcę wykonywać wszystkie te operacje w silniku bazy danych, a nie ładować danych w zewnętrznej aplikacji do obliczania MD5, ponieważ niektóre tabele mają ogromny zestaw danych.

wersja serwera SQL 2005 PostgreSQL wersja 9.1

+1

nie jestem pewien, że faktycznie korzysta UCS-2 (lub UTF-16) po stronie serwera SQL. '무' w utf-8 (hex) to 'EB AC B4', w utf-16be (i ucs-2be) to' BB 34', aw UHC to 'B9 AB'. Tylko ostatni daje ci hash, wspomniałeś (i oczekujesz) w swoich przykładach. – pozs

+0

@pozs, To interesująca obserwacja. Wyglądam jak w sql-server: N '무' zapewni –

+0

@pozs, To interesująca obserwacja. Wygląda jak w sql-server 'select N '무'' zapewni kodowanie ucs-2 przy użyciu typu nvarchar. 'select '무'' domyślnie varchar, a sekwencja bitów znaków jest określana przez sortowanie bazy danych. W moim przypadku sortowanie bazy danych sql-server jest _Korean_Wansung_CI_AS_. Wygląda na to, że sekwencja bitów znaków dla tego sortowania jest podobna do UHC. Ale w takim przypadku, jeśli chińskie znaki są obsługiwane poprawnie przez to sortowanie, dlaczego nie jest obsługiwane przez kodowanie UHC w PostgreSQL? Albo jakiego kodowania powinienem użyć w postgresie, aby obsłużyć chińskie znaki? –

Odpowiedz

3

Niestety, PostgreSQL nie obsługuje UTF-16/UCS-2 albo.

Ale można napisać funkcję, aby przekonwertować utf8 text do UCS2 danych binarnych (bytea):

create or replace function text_to_ucs2be(input_in_utf8 text) 
    returns bytea 
    immutable 
    strict 
    language sql 
as $$ 
    select decode(string_agg(case 
      when code_point < 65536 
      then lpad(to_hex(code_point), 4, '0') 
     end, ''), 'hex') 
    from regexp_split_to_table(input_in_utf8, '') chr, 
     ascii(chr) code_point 
$$; 

create or replace function text_to_ucs2le(input_in_utf8 text) 
    returns bytea 
    immutable 
    strict 
    language sql 
as $$ 
    select decode(string_agg(case 
      when code_point < 65536 
      then lpad(to_hex(code_point & 255), 2, '0') 
      || lpad(to_hex(code_point >> 8), 2, '0') 
     end, ''), 'hex') 
    from regexp_split_to_table(input_in_utf8, '') chr, 
     ascii(chr) code_point 
$$; 

Uwaga: funkcje te powyżej obetnie się Obojętnie Nie BMP kodowe punktów (a więc nazwa w nich ucs2).

Poniższe stwierdzenia powinno dać takie same wyniki:

-- on PostgreSQL 
select md5(text_to_ucs2le('무')); 

-- on SQL server 
select master.dbo.fn_varbintohexstr(HashBytes('MD5', N'무')); 
+0

Dzięki. Myślę, że to jest sposób, aby iść do przodu. –

+0

@LalitKumarBhasin Jak poszło - użyłeś tego podejścia lub czegoś innego? –

+0

@ SveinFidjestøl Tak, użyłem tego podejścia z kilkoma drobnymi poprawkami. Zadziałało. –

1

Powinno być możliwe do realizacji funkcji NVARCHAR_TO_UTF8 w SQL Server 2005 w oparciu o kod podany w tym poście.

SQL FUNCTION TO GET NVARCHAR FROM UTF-8 STORED IN VARCHAR

Trzeba by wdrożyć konwersji odwrotnej.

Przy użyciu funkcji NVARCHAR_TO_UTF8 powinno być możliwe obliczenie tego samego skrótu MD5 w SQL Server 2005, jak w PostgreSQL 9.1.

Należy pamiętać, że rodzimy UTF-8 jest nadal nieobecny jak SQL Server 2014: UTF-8 Feature Suggestion at Microsoft Connect

+0

Dzięki. Bardzo doceniane. Wygląda na to, że jest to droga do wyjścia z serwera SQL, ponieważ brakuje obsługi UTF-8. Zanim zaakceptuję to rozwiązanie, poczekam na sprawdzenie, czy istnieje lepsze rozwiązanie z perspektywy PostgreSQL. –

Powiązane problemy