Чтение 64-х битных целых чисел из Oracle на OCCI (MSVC)

от автора

OCCI (Oracle C++ Call Interface) — достаточно удобная объектно-ориентированная оболочка над OCI (Oracle Call Interface), позволяющая подключаться и работать с СУБД Oracle из C++ практически с той же легкостью, какую обеспечивают интерпретируемые языки программирования вроде Perl. Однако, OCCI имеет и свои недостатки. В частности, числа в Oracle представлены типом Number, обеспечивающим максимальную точность до 38 значащих цифр, но в OCCI нет метода, позволяющего преобразовать этот тип в 64-х битное целое число. Есть возможность преобразовывать в обычное целое число (32 бита), в double (число с плавающей точкой, 64 бита) и long double (число с плавающей точкой, 80 бит (по стандарту С)), но не в 64-х битное целое.

Вариант с преобразованием в double и последующим преобразованием в long long может устроить многих пользователей, но далеко не всех, так как он обеспечивает только 52-х битную точность (хранимая мантисса числа в типе double имеет размер 52 бита) и, как следствие, возможную потерю точности при больших числах. Казалось бы, проблему могло бы решить промежуточное преобразование в long double, но здесь в игру вступает ограничение компилятора от Microsoft — он не поддерживает 80-ти битные числа с плавающей точкой и воспринимает тип long double аналогом типа double.

В то же время OCI, лежащий в основе OCCI, проводить преобразование из Number в long long умеет. Для этого в нем есть следующая функция:

sword OCINumberToInt (OCIError *err, const OCINumber *number, uword rsl_length, uword rsl_flag, void *rsl); 

Функция принимает в параметрах указатель на исходное число const OCINumber *number, на результирующее число void *rsl, а также размер результирующего числа в байтах uword rsl_length. И среди поддерживаемых размеров результата есть 8, т.е. 64 бита. Теперь остается только получить OCINumber.

К сожалению, здесь мы сталкиваемся со следующей проблемой — в OCCI из ResultSet (класс, представляющий собой результат выполнения запроса) получить OCINumber также возможности нет, и класс oracle::occi::Number, представляющий собой оболочку над OCINumber, тоже не предоставляет доступа к своим внутренностям, объявив их как private:

private:   /* Private constructor for constructing number from methods inside  */   OCINumber getOCINumber() const;    OCINumber data; 

И вот здесь на помощь нам приходит возможность преобразования совершенно разных типов друг к другу в C++. Поскольку класс Number не имеет виртуальных методов, а член OCINumber data является первым в классе, адрес этого члена в памяти совпадает с адресом экземпляра класса! Т.е. нам достаточно написать:

oracle::occi::Number number; OCINumber *ociNumber = (OCINumber*)&number; 

В результате функция преобразования Number в 64-х битное целое число (для MSVC) выглядит следующим образом:

__int64 OCCINumberToInt64 (const oracle::occi::Number &number, OCIError *hError, bool bSigned) { 	const OCINumber *ociNumber = (const OCINumber*)&number; 	__int64 result; 	OCINumberToInt (hError, ociNumber, 8, bSigned ? OCI_NUMBER_SIGNED : OCI_NUMBER_UNSIGNED, &result);  	return result; } 

А чтобы получить необходимый функции OCI Error Handle, можно воспользоваться следующим кодом:

oracle::occi::Environment env; ... OCIEnv *hEnv = env->getOCIEnvironment (); OCIError *hError = 0; OCIHandleAlloc (hEnv, (void**)&hError, OCI_HTYPE_ERROR, 0, 0);  ...  OCIHandleFree (hError, OCI_HTYPE_ERROR); 

ссылка на оригинал статьи http://habrahabr.ru/post/259015/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *