Я делаю привязку к OCI
для Rust
и столкнулся с проблемой чтения данных с [N]CLOB
. По непонятной причине OCILobRead[2]
не заполняет весь предоставленный буфер данными, хотя кодировка сервера и клиента совпадает, то есть преобразование не должно происходить. Тем не менее, по какой-то причине Oracle по каждому символу резервирует в буфере ровно 4 байта, даже при чтении символов, занимающих один байт в запрошенной кодировке. Это приводит к тому, что требуется четырехкратное резервирование в буфере, которое крайне неэффективно. Кроме того, это приводит к тому, что в буфере размером менее 4 байтов ничего не читается вообще (а в случае греческих символов, например, даже 7 байт недостаточно для их чтения, а в Windows с использованием такого буфера приводят к сбою приложения. И на * nixes в этом случае Oracle возвращает число прочитанных байтов - 0 и количество прочитанных символов - 1. WTF? Однако, если увеличить размер буфера, то после прочтения символ занимает в нем всего 4 байта (кодировка UTF-8)).Oracle не использует выходной буфер полностью в OCILobRead2, возможно, это исправить?
Я вижу, что Oracle пытается не разбить один символ в середине последовательности байтов в запрошенной кодировке (таким образом, буфер всегда заполняется или N-й байт на символ или 0 байтов), но он делает это странным образом ,
Есть ли возможность заставить Oracle использовать буфер максимально? Может быть, где-то еще нужно указать кодировку? Я, судя по всему, пробовал уже все варианты и ничего не помогает.
Сервер и клиентская кодировка одинаковы - AL32UTF8
. Здесь примерно так происходит последовательность вызовов OCI (фактический код слишком длинный и сложный для демонстрации здесь, но вы можете найти полный тестовый код в my repo. Требуется некоторый язык программирования или язык ржавчины. Данные испытаний подготовлены в script):
ub2 encoding = 873;// AL32UTF8, strange, but there no define constants in API
// For: CLOB NCLOB
OCIEnvNlsCreate(..., encoding, encoding);
...
OCIStmtPrepare2(...);
OCILobLocator* lob;
OCIDefineByPos(..., &lob, ...);
OCIExecute(...);
OCIStmtFetch2(...);
char buf[4];
// request read to end of CLOB
ub8 bytes = 0;
ub8 chars = 0;
OCILobRead2(..., lob,
&bytes, &chars,
1, &buf, 4,
OCI_FIRST_PIECE,
0, 0,// callbacks not used
encoding, SQLCS_IMPLICIT
);
// when CLOB contains "" this call read only "0", not "0123"
// though the place in the buffer is enough and oracle knows about it
Это действительно проблема? Обычно вы хотите уменьшить количество раундов между приложением и db. Поэтому вы вызываете OCILobGetChunkSize и создаете буфер соответствующего размера. – ibre5041
Преобразование кодировки происходит на стороне клиента. База данных не знает, как преобразуется строка, возможно, вы можете преобразовать ее в UCS-32. Сервер базы данных знает только размер буфера памяти приема. Это было нормально, пока не были введены многобайтовые кодировки. Это не слишком удобно, но в любом случае вы не хотите читать символ CLOB char. – ibre5041
> Итак, вы вызываете OCILobGetChunkSize и подготавливаете буфер соответствующего размера. Я пишу библиотеку, а не приложение, поэтому пользователи библиотеки получат ожидаемое поведение. Кажется, что необходимо полагаться на комментарии и надеяться, что пользователи не будут стрелять в себя на ноге. Oracle API читает CLOB только по каждому символу. В нем и проблема - даже если достаточно места в буфере, сервер слишком пессимистичен, он использует. В экстренном случае в буфере вообще ничего не будет написано, хотя могло бы, или на Windows произойдет сбой приложения, которое вообще не подходит – Mingun