2017-02-05 14 views
0

Я делаю привязку к 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 
+0

Это действительно проблема? Обычно вы хотите уменьшить количество раундов между приложением и db. Поэтому вы вызываете OCILobGetChunkSize и создаете буфер соответствующего размера. – ibre5041

+0

Преобразование кодировки происходит на стороне клиента. База данных не знает, как преобразуется строка, возможно, вы можете преобразовать ее в UCS-32. Сервер базы данных знает только размер буфера памяти приема. Это было нормально, пока не были введены многобайтовые кодировки. Это не слишком удобно, но в любом случае вы не хотите читать символ CLOB char. – ibre5041

+0

> Итак, вы вызываете OCILobGetChunkSize и подготавливаете буфер соответствующего размера. Я пишу библиотеку, а не приложение, поэтому пользователи библиотеки получат ожидаемое поведение. Кажется, что необходимо полагаться на комментарии и надеяться, что пользователи не будут стрелять в себя на ноге. Oracle API читает CLOB только по каждому символу. В нем и проблема - даже если достаточно места в буфере, сервер слишком пессимистичен, он использует. В экстренном случае в буфере вообще ничего не будет написано, хотя могло бы, или на Windows произойдет сбой приложения, которое вообще не подходит – Mingun

ответ

0

Вы установили параметр 'bufl' в 4, который выражается в байтах. Таким образом, клиент оракула получает только 1 utf8 char (так как он может принимать до 4 байтов). Поведение функции кажется мне логичным.

+0

> как это может занять до 4 байтов Может, но не взять. Oracle работает не с абстрактным например, когда я использую UTF-16, он корректно возвращает как можно больше символов, как суррогатных, так и обычных символов. Итак, почему UTF-8 должен быть другим?Сервер подсчитывает символы при подготовке ответа к клиенту (поскольку это единственный способ не попасть в середину символа) и известно, сколько байтов заполняет буфер. Так почему он не может заполнить byffer как можно больше? – Mingun

+0

> Сервер подсчитывает символы при подготовке ответа к клиенту. Нет, это не так. Преобразование Charset происходит на стороне клиента :(Сервер знает только размер входного буфера приемника, а не количество символов, которые нужно прочитать. Пока не были введены многобайтовые кодировки, это имело смысл. – ibre5041

+0

Сервер не знает номера вызывающего абонента Целевая кодировка – ibre5041