python-cx_Oracle/Transforms.c
2016-03-07 08:01:24 -07:00

483 lines
17 KiB
C

//-----------------------------------------------------------------------------
// Transforms.c
// Provides methods for transforming Oracle data to Python objects or for
// setting Oracle data from Python objects.
//-----------------------------------------------------------------------------
static udt_VariableType vt_Date;
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
//-----------------------------------------------------------------------------
// OracleBooleanToPythonBoolean()
// Return a Python boolean object given an Oracle boolean.
//-----------------------------------------------------------------------------
static PyObject *OracleBooleanToPythonBoolean(
boolean *value) // value to convert
{
PyObject *pythonValue;
pythonValue = (*value) ? Py_True : Py_False;
Py_INCREF(pythonValue);
return pythonValue;
}
#endif
//-----------------------------------------------------------------------------
// OracleDateToPythonDate()
// Return a Python date object given an Oracle date.
//-----------------------------------------------------------------------------
static PyObject *OracleDateToPythonDate(
udt_VariableType *varType, // variable type
OCIDate* value) // value to convert
{
ub1 hour, minute, second, month, day;
sb2 year;
OCIDateGetDate(value, &year, &month, &day);
OCIDateGetTime(value, &hour, &minute, &second);
if (varType == &vt_Date)
return PyDate_FromDate(year, month, day);
return PyDateTime_FromDateAndTime(year, month, day, hour, minute, second,
0);
}
//-----------------------------------------------------------------------------
// OracleIntervalToPythonDelta()
// Return a Python delta object given an Oracle interval.
//-----------------------------------------------------------------------------
static PyObject *OracleIntervalToPythonDelta(
udt_Environment *environment, // environment
OCIInterval *value) // value to convert
{
sb4 days, hours, minutes, seconds, fseconds;
sword status;
status = OCIIntervalGetDaySecond(environment->handle,
environment->errorHandle, &days, &hours, &minutes, &seconds,
&fseconds, value);
if (Environment_CheckForError(environment, status,
"OracleIntervalToPythonDelta()") < 0)
return NULL;
seconds = hours * 60 * 60 + minutes * 60 + seconds;
return PyDelta_FromDSU(days, seconds, fseconds / 1000);
}
//-----------------------------------------------------------------------------
// OracleTimestampToPythonDate()
// Return a Python date object given an Oracle timestamp.
//-----------------------------------------------------------------------------
static PyObject *OracleTimestampToPythonDate(
udt_Environment *environment, // environment
OCIDateTime* value) // value to convert
{
ub1 hour, minute, second, month, day;
sword status;
ub4 fsecond;
sb2 year;
status = OCIDateTimeGetDate(environment->handle, environment->errorHandle,
value, &year, &month, &day);
if (Environment_CheckForError(environment, status,
"OracleTimestampToPythonDate(): date portion") < 0)
return NULL;
status = OCIDateTimeGetTime(environment->handle, environment->errorHandle,
value, &hour, &minute, &second, &fsecond);
if (Environment_CheckForError(environment, status,
"OracleTimestampToPythonDate(): time portion") < 0)
return NULL;
return PyDateTime_FromDateAndTime(year, month, day, hour, minute, second,
fsecond / 1000);
}
//-----------------------------------------------------------------------------
// OracleNumberToPythonFloat()
// Return a Python float given an Oracle number.
//-----------------------------------------------------------------------------
static PyObject *OracleNumberToPythonFloat(
udt_Environment *environment, // environment
OCINumber* value) // value to convert
{
double doubleValue;
sword status;
status = OCINumberToReal(environment->errorHandle,
value, sizeof(double), (dvoid*) &doubleValue);
if (Environment_CheckForError(environment, status,
"OracleNumberToPythonFloat()") < 0)
return NULL;
return PyFloat_FromDouble(doubleValue);
}
//-----------------------------------------------------------------------------
// OracleNumberToPythonInteger()
// Return a Python integer given an Oracle number.
//-----------------------------------------------------------------------------
static PyObject *OracleNumberToPythonInteger(
udt_Environment *environment, // environment
OCINumber* value) // value to convert
{
long integerValue;
sword status;
status = OCINumberToInt(environment->errorHandle, value,
sizeof(long), OCI_NUMBER_SIGNED, (dvoid*) &integerValue);
if (Environment_CheckForError(environment, status,
"OracleNumberToPythonInteger()") < 0)
return NULL;
return PyInt_FromLong(integerValue);
}
#if ORACLE_VERSION_HEX >= ORACLE_VERSION(12, 1)
//-----------------------------------------------------------------------------
// PythonBooleanToOracleBoolean()
// Transform a Python boolean into an Oracle boolean.
//-----------------------------------------------------------------------------
static int PythonBooleanToOracleBoolean(
PyObject *pythonValue, // Python value to convert
boolean *oracleValue) // value to convert
{
*oracleValue = (pythonValue == Py_True);
return 0;
}
#endif
//-----------------------------------------------------------------------------
// PythonBooleanToOracleNumber()
// Transform a Python boolean into an Oracle number.
//-----------------------------------------------------------------------------
static int PythonBooleanToOracleNumber(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value to convert
OCINumber *oracleValue) // Oracle value to set
{
long integerValue;
sword status;
integerValue = (pythonValue == Py_True);
status = OCINumberFromInt(environment->errorHandle, &integerValue,
sizeof(long), OCI_NUMBER_SIGNED, oracleValue);
return Environment_CheckForError(environment, status,
"PythonBooleanToOracleNumber()");
}
//-----------------------------------------------------------------------------
// PythonDateToOracleDate()
// Transform a Python date into an Oracle date.
//-----------------------------------------------------------------------------
static int PythonDateToOracleDate(
PyObject *pythonValue, // Python value to convert
OCIDate *oracleValue) // Oracle value to set
{
ub1 month, day, hour, minute, second;
sb2 year;
if (PyDateTime_Check(pythonValue)) {
year = (short) PyDateTime_GET_YEAR(pythonValue);
month = PyDateTime_GET_MONTH(pythonValue);
day = PyDateTime_GET_DAY(pythonValue);
hour = PyDateTime_DATE_GET_HOUR(pythonValue);
minute = PyDateTime_DATE_GET_MINUTE(pythonValue);
second = PyDateTime_DATE_GET_SECOND(pythonValue);
} else if (PyDate_Check(pythonValue)) {
year = (short) PyDateTime_GET_YEAR(pythonValue);
month = PyDateTime_GET_MONTH(pythonValue);
day = PyDateTime_GET_DAY(pythonValue);
hour = minute = second = 0;
} else {
PyErr_SetString(PyExc_TypeError, "expecting date data");
return -1;
}
OCIDateSetDate(oracleValue, year, month, day);
OCIDateSetTime(oracleValue, hour, minute, second);
return 0;
}
#if PY_MAJOR_VERSION < 3
//-----------------------------------------------------------------------------
// PythonIntegerToOracleNumber()
// Transform a Python integer into an Oracle number.
//-----------------------------------------------------------------------------
static int PythonIntegerToOracleNumber(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value to convert
OCINumber *oracleValue) // Oracle value to set
{
long integerValue;
sword status;
integerValue = PyInt_AS_LONG(pythonValue);
status = OCINumberFromInt(environment->errorHandle, &integerValue,
sizeof(long), OCI_NUMBER_SIGNED, oracleValue);
return Environment_CheckForError(environment, status,
"PythonIntegerToOracleNumber()");
}
#endif
//-----------------------------------------------------------------------------
// PythonFloatToOracleNumber()
// Transform a Python float into an Oracle number.
//-----------------------------------------------------------------------------
static int PythonFloatToOracleNumber(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value to convert
OCINumber *oracleValue) // Oracle value to set
{
double doubleValue;
sword status;
doubleValue = PyFloat_AS_DOUBLE(pythonValue);
status = OCINumberFromReal(environment->errorHandle, &doubleValue,
sizeof(double), oracleValue);
return Environment_CheckForError(environment, status,
"PythonFloatToOracleNumber()");
}
//-----------------------------------------------------------------------------
// PythonLongToOracleNumber()
// Set the value of the variable from a Python long.
//-----------------------------------------------------------------------------
static int PythonLongToOracleNumber(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value to convert
OCINumber *oracleValue) // Oracle value to set
{
udt_Buffer textBuffer;
PyObject *textValue;
sword status;
textValue = PyObject_Str(pythonValue);
if (!textValue)
return -1;
if (cxBuffer_FromObject(&textBuffer, textValue, environment->encoding) < 0)
return -1;
status = OCINumberFromText(environment->errorHandle,
(text*) textBuffer.ptr, textBuffer.size,
(text*) environment->numberFromStringFormatBuffer.ptr,
environment->numberFromStringFormatBuffer.size, NULL, 0,
oracleValue);
cxBuffer_Clear(&textBuffer);
Py_DECREF(textValue);
return Environment_CheckForError(environment, status,
"PythonLongToOracleNumber()");
}
//-----------------------------------------------------------------------------
// GetFormatAndTextFromPythonDecimal()
// Return the number format and text to use for the Decimal object.
//-----------------------------------------------------------------------------
static int GetFormatAndTextFromPythonDecimal(
PyObject *tupleValue, // decimal as_tuple() value
PyObject **textObj, // text string for conversion
PyObject **formatObj) // format for conversion
{
long numDigits, scale, i, sign, length, digit;
char *textValue, *format, *textPtr, *formatPtr;
PyObject *digits;
// acquire basic information from the value tuple
sign = PyInt_AsLong(PyTuple_GET_ITEM(tupleValue, 0));
if (PyErr_Occurred())
return -1;
digits = PyTuple_GET_ITEM(tupleValue, 1);
scale = PyInt_AsLong(PyTuple_GET_ITEM(tupleValue, 2));
if (PyErr_Occurred())
return -1;
numDigits = PyTuple_GET_SIZE(digits);
// allocate memory for the string and format to use in conversion
length = numDigits + abs(scale) + 3;
textValue = textPtr = PyMem_Malloc(length);
if (!textValue) {
PyErr_NoMemory();
return -1;
}
format = formatPtr = PyMem_Malloc(length);
if (!format) {
PyMem_Free(textValue);
PyErr_NoMemory();
return -1;
}
// populate the string and format
if (sign)
*textPtr++ = '-';
for (i = 0; i < numDigits + scale; i++) {
*formatPtr++ = '9';
if (i < numDigits) {
digit = PyInt_AsLong(PyTuple_GetItem(digits, i));
if (PyErr_Occurred()) {
PyMem_Free(textValue);
return -1;
}
}
else digit = 0;
*textPtr++ = '0' + (char) digit;
}
if (scale < 0) {
*formatPtr++ = 'D';
*textPtr++ = '.';
for (i = scale; i < 0; i++) {
*formatPtr++ = '9';
if (numDigits + i < 0)
digit = 0;
else {
digit = PyInt_AsLong(PyTuple_GetItem(digits, numDigits + i));
if (PyErr_Occurred()) {
PyMem_Free(textValue);
return -1;
}
}
*textPtr++ = '0' + (char) digit;
}
}
*formatPtr = '\0';
*textPtr = '\0';
*textObj = cxString_FromAscii(textValue);
PyMem_Free(textValue);
if (!*textObj) {
PyMem_Free(format);
return -1;
}
*formatObj = cxString_FromAscii(format);
PyMem_Free(format);
if (!*formatObj) {
Py_DECREF(*textObj);
return -1;
}
return 0;
}
//-----------------------------------------------------------------------------
// PythonDecimalToOracleNumber()
// Transform a Python decimal object into an Oracle number.
//-----------------------------------------------------------------------------
static int PythonDecimalToOracleNumber(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value to convert
OCINumber *oracleValue) // Oracle value to set
{
PyObject *textValue, *format, *tupleValue;
udt_Buffer textBuffer, formatBuffer;
sword status;
tupleValue = PyObject_CallMethod(pythonValue, "as_tuple", NULL);
if (!tupleValue)
return -1;
if (GetFormatAndTextFromPythonDecimal(tupleValue, &textValue,
&format) < 0) {
Py_DECREF(tupleValue);
return -1;
}
Py_DECREF(tupleValue);
if (cxBuffer_FromObject(&textBuffer, textValue, environment->encoding) < 0)
return -1;
if (cxBuffer_FromObject(&formatBuffer, format,
environment->encoding) < 0) {
cxBuffer_Clear(&textBuffer);
return -1;
}
status = OCINumberFromText(environment->errorHandle,
(text*) textBuffer.ptr, textBuffer.size, (text*) formatBuffer.ptr,
formatBuffer.size, environment->nlsNumericCharactersBuffer.ptr,
environment->nlsNumericCharactersBuffer.size, oracleValue);
cxBuffer_Clear(&textBuffer);
cxBuffer_Clear(&formatBuffer);
Py_DECREF(textValue);
Py_DECREF(format);
return Environment_CheckForError(environment, status,
"PythonDecimalToOracleNumber()");
}
//-----------------------------------------------------------------------------
// PythonNumberToOracleNumber()
// Convert a Python number to an Oracle number.
//-----------------------------------------------------------------------------
static int PythonNumberToOracleNumber(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value
OCINumber* oracleValue) // Oracle value
{
#if PY_MAJOR_VERSION < 3
if (PyInt_Check(pythonValue))
return PythonIntegerToOracleNumber(environment, pythonValue,
oracleValue);
#endif
if (PyBool_Check(pythonValue))
return PythonBooleanToOracleNumber(environment, pythonValue,
oracleValue);
if (PyLong_Check(pythonValue))
return PythonLongToOracleNumber(environment, pythonValue, oracleValue);
if (PyFloat_Check(pythonValue))
return PythonFloatToOracleNumber(environment, pythonValue,
oracleValue);
if (Py_TYPE(pythonValue) == g_DecimalType)
return PythonDecimalToOracleNumber(environment, pythonValue,
oracleValue);
PyErr_SetString(PyExc_TypeError, "expecting numeric data");
return -1;
}
//-----------------------------------------------------------------------------
// PythonDateToOracleTimestamp()
// Convert a Python date to an Oracle timestamp.
//-----------------------------------------------------------------------------
static int PythonDateToOracleTimestamp(
udt_Environment *environment, // environment
PyObject *pythonValue, // Python value
OCIDateTime* oracleValue) // Oracle value
{
sword status;
uword valid;
// make sure a timestamp is being bound
if (!PyDateTime_Check(pythonValue)) {
PyErr_SetString(PyExc_TypeError, "expecting timestamp data");
return -1;
}
// store a copy of the value
status = OCIDateTimeConstruct(environment->handle,
environment->errorHandle, oracleValue,
(sb2) PyDateTime_GET_YEAR(pythonValue),
PyDateTime_GET_MONTH(pythonValue),
PyDateTime_GET_DAY(pythonValue),
PyDateTime_DATE_GET_HOUR(pythonValue),
PyDateTime_DATE_GET_MINUTE(pythonValue),
PyDateTime_DATE_GET_SECOND(pythonValue),
PyDateTime_DATE_GET_MICROSECOND(pythonValue) * 1000, NULL, 0);
if (Environment_CheckForError(environment, status,
"PythonDateToOracleTimestamp(): create structure") < 0)
return -1;
status = OCIDateTimeCheck(environment->handle, environment->errorHandle,
oracleValue, &valid);
if (Environment_CheckForError(environment, status,
"PythonDateToOracleTimestamp(): check validity") < 0)
return -1;
if (valid != 0) {
PyErr_SetString(g_DataErrorException, "invalid date");
return -1;
}
return 0;
}