📜 ⬆️ ⬇️

Reading 64-bit integers from Oracle to OCCI (MSVC)

OCCI (Oracle C ++ Call Interface) is a fairly convenient object-oriented wrapper on OCI (Oracle Call Interface), which allows you to connect and work with Oracle C ++ databases with almost the same ease as interpreted programming languages ​​like Perl provide. However, OCCI has its drawbacks. In particular, numbers in Oracle are represented by the Number type, which provides maximum accuracy up to 38 significant digits, but OCCI does not have a method to convert this type to a 64-bit integer. It is possible to convert to a regular integer (32 bits), to double (a floating point number, 64 bits) and a long double (a floating point number, 80 bits (according to standard C)), but not to a 64-bit integer.

The variant with conversion to double and subsequent conversion to long can suit many users, but not all, as it provides only 52-bit accuracy (the stored mantissa of the double type has a size of 52 bits) and, as a result, the possible loss of accuracy with large numbers. It would seem that the problem could be solved by an intermediate conversion to a long double, but here the compiler restriction from Microsoft comes into play - it does not support 80-bit floating point numbers and perceives the type long double as a double type.

At the same time, OCI, which is the basis of OCCI, is able to convert from Number to long long. For this, it has the following function:

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

The function takes in the parameters a pointer to the initial number const OCINumber * number , to the resulting number void * rsl , and also the size of the resulting number in bytes uword rsl_length . And among the supported sizes of the result there are 8, i.e. 64 bits. Now it only remains to get OCINumber.
')
Unfortunately, here we encounter the following problem - in the OCCI of the ResultSet (the class that is the result of the query), it is also impossible to get OCINumber, and the oracle :: occi :: Number class, which is a wrapper over OCINumber, also does not provide access to its entrails, declaring them private:

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

And here we can come to the aid of the possibility of converting completely different types to each other in C ++. Since the Number class has no virtual methods, and the OCINumber data member is the first in the class, the address of this member in memory is the same as the address of the class instance! Those. we just need to write:

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

As a result, the Number to 64-bit integer conversion function (for MSVC) looks like this:

 __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; } 

And to get the necessary OCI Error Handle functions, you can use the following code:

 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); 

Source: https://habr.com/ru/post/259015/


All Articles