Как указать, что длина 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 шт):

Автор решения: 0xdb

Приведенный пример в вопросе рабочий:

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

→ Ссылка