Отображение шестнадцатеричного значения строки из oracle varchar2?

У нас возникают проблемы с текстом, который кодируется несколькими способами, но хранится в одном столбце таблицы. Длинная история. В MySQL я могу "выбрать hex (str) из таблицы where", и я вижу байты строки точно так, как я их устанавливал.

В Oracle у меня есть строка, которая начинается с турецкого символа İ, который является символом Юникода 0x0130 "ЛАТИНСКОЕ КАПИТАЛОВОЕ ПИСЬМО С ДВОЙНОЙ ВЫШЕ". Это в моей печатной копии книги Unicode версии 2.0. В UTF-8 этот символ равен 0xc4b0.

У нас очень старые клиентские приложения, которые нам нужно поддерживать. Они отправят нам этот текст в "windows-1254". Раньше мы просто закрывали глаза, хранили его и отдавали обратно. Теперь нам нужен Unicode, или нам дается Unicode.

Итак, у меня есть:

SQL> select id, name from table where that thing;

ID     NAME
------ ------------------------
746    Ý

Это имеет смысл, потому что "İ" - это 0xdd в windows-1254, а 0xdd в wondows-1252 - "Ý". Мой терминал предположительно установлен на обычный Windows-1252.

Но:

SQL> select id, rawtohex(name) from table where that thing;

ID     RAWTOHEX(NAME)
------ ------------------------
746    C39D

Кажется, что нет никакого эквивалента функции hex (name) в MySQL. Но я должен что-то упустить. Что мне здесь не хватает?

Мой код Java должен принять utf8, который мне предоставляется, и сохранить копию utf8 и копию окна-1252. Код java дает мне:

bytes (utf8):  c4 b0
bytes (1254):  dd

Тем не менее, когда я его сохраняю, клиент не получает правильный символ. И когда я пытаюсь понять, что на самом деле хранит Oracle, я получаю мусор, увиденный выше. Я понятия не имею, откуда C39D. Любые предложения?

У нас есть ojdbc14.jar, встроенный во все наши приложения, и мы подключаемся к базе данных, которая говорит, что это "Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production".

Ответ 1

Используйте функцию dump, чтобы увидеть, как Oracle хранит данные внутренне.

У вас, похоже, есть недоразумение относительно того, как Oracle обрабатывает символы VARCHAR2, которые задают преобразования: вы не можете влиять на то, как Oracle сохраняет свои данные физически. (Также, если вы еще этого не сделали, полезно прочитать: Абсолютный минимум Каждый разработчик программного обеспечения абсолютно должен положительно знать о юникодном и символьном наборах).

Ваш клиент говорит Oracle только в двоичном формате. Фактически все системы обмениваются информацией только в двоичном формате. Чтобы понимать друг друга, необходимо, чтобы обе системы знали, какой язык (набор символов) используется.

В вашем случае мы можем восстановить то, что происходит:

  • Ваш клиент отправляет байт dd в Oracle и говорит, что это windows-1252 (вместо 1254).
  • Oracle ищет свою таблицу набора символов и видит, что эти данные переводятся в символ Ý в этом наборе символов.
  • Oracle логически сохраняет эту информацию в своей таблице.
  • Поскольку Oracle настроен в UTF-8, он преобразует эти данные в двоичную репликацию UTF-8 Ý:

    SQL> SELECT rawtohex('Ý') FROM dual;
    
    RAWTOHEX('Ý')
    --------------
    C39D
    
  • Oracle хранит C39D внутренне.

Как вы можете видеть, проблема возникает с первого шага: есть проблема настройки. Пока вы не исправите это, системы не смогут успешно вести диалог.

Преобразование автоматически при использовании VARCHAR2, потому что этот тип данных является логическим текстовым интерфейсом (у вас нет контроля над тем, чтобы заставлять фактические двоичные данные сохраняться).

Ответ 2

У меня есть байты в UTF-8, чтобы начать.

String strFromUTF8 = new String(bytes, "UTF8");
byte[] strInOldStyle = strFromUTF8.getBytes("Cp1254");

С MySQL я закончил. Я беру эти байты, превращаю их в шестнадцатеричную строку и выполняю обновление с помощью unhex (hexStr). Это позволяет мне помещать устаревшие байты в столбцы varchar.

С Oracle я должен сделать:

String again = new String(strInOldStyle, "Cp1254");
byte[] nextOldBytes = again.getBytes("UTF8");

Теперь я могу сделать обновление и получить байты в столбце varchar2 с помощью:

update table set colName = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('hexStr')) where ...

Странно, нет? Я уверен, что сделал это более сложным, чем нужно.

Что мы видим, это, тем не менее,

"İ" in UTF-8 == 0xc4d0
"İ" in Cp1254 == 0xdd == "Ý" in Cp1252
"Ý" in UTF-8 == 0xc3d9

Итак, если я получаю строку "İ" и делаю:

update table set name = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('C3D9')) where ...

Затем наш старый клиент дает нам "İ". Ага. Он работает.