Как указать, что длина VARCHAR2 для текста юникода в символах, а не в байтах?
Соглсано этой спецификации VARCHAR2(max_size CHAR) должен хранить max_size кол-во символов. Но наблюдаю другое/противоположенное поведение для текстов с юникод символами.
Например, следуюий пример:
create table test (id varchar2(3 char) not null, primary key (id));
insert into test(id) values('abc');
1 row(s) affected.
insert into test(id) values('ффф');
Query 1 ERROR: ORA-12899: value too large for column "TEST"."TEST"."ID" (actual: 6, maximum: 3)
Получается, что VARCHAR2(3 chars) на самом деле то же самое, что и байт? Но это же НЕ ТАК:
create table test (id varchar2(3 byte) not null, primary key (id));
insert into test(id) values('ффф');
Query 1 ERROR: ORA-12899: value too large for column "OSB_TRUNK"."TEST"."ID" (actual: 18, maximum: 3)
И вопрос остается, как указать, что длина VARCHAR2 для текста юникода в символах (точнее UTF8)?
PS Использую TablePlus, DBeaver или JDBC (thin) драйвер. Дамп после вставки:
Typ=1 Len=6 CharacterSet=AL32UTF8: ef,bf,bd,ef,bf,bd
Свободный перевод вопроса Oracle varchar2 and unicode texts от участника @FoxyBOA
Ответы (1 шт):
Приведенный пример в вопросе рабочий:
SQL> select * from v$nls_parameters where parameter = 'nls_characterset'
/
PARAMETER VALUE
------------------- -----------
NLS_CHARACTERSET AL32UTF8
SQL> create table test (id varchar2 (3 char));
Table created.
SQL> insert into test(id) values('abc');
1 row created.
SQL> insert into test(id) values('ффф');
1 row created.
Похоже, используемый клиент использует неправильные настройки кодировки символов.
Символ: ф (U+0444: Cyrillic Small Letter Ef), имеет следующие значения:
+-------------------------------------------------------------------------------+
|Encoding|hex |dec (bytes)|dec |binary |
+-------------------------------------------------------------------------------+
|UTF-8 |D1 84 |209 132 |53636 |11010001 10000100 |
|UTF-16BE|04 44 |4 68 |1092 |00000100 01000100 |
|UTF-16LE|44 04 |68 4 |17412 |01000100 00000100 |
|UTF-32BE|00 00 04 44|0 0 4 68 |1092 |00000000 00000000 00000100 01000100|
|UTF-32LE|44 04 00 00|68 4 0 0 |1141112832|01000100 00000100 00000000 00000000|
+-------------------------------------------------------------------------------+
То есть, DUMP должен вернуть:
Typ=1 Len=6 CharacterSet=AL32UTF8: d1,84,d1,84,d1,84
Но в действительноости в дампе символ - ef,bf,bd, U+FFFD: Replacement Character
То есть, вставка значения ффф, конвертируется в ���.
Полагаю, что на самом деле клиент использует UTF-8, но не устанавливает эту кодировку для сессии БД. Поэтому, используется кодировка по умолчанию US7ASCII. Клиент посылает 6 байт (d1,84,d1,84,d1,84), но они интерпретируются как 6 однобайтных символов, один из которых недействительный.
Обычно, используется переменная окружения NLS_LANG, чтобы установить кодировку. Однако, DBeaver использует Java/JDBC, который не использует настройки NLS_LANG (по крайней мере, по умолчанию).
Свободный перевод ответа от участника Wernfried Domscheit