diff --git a/BooleanVar.c b/BooleanVar.c index a089b4d..6f8ed33 100644 --- a/BooleanVar.c +++ b/BooleanVar.c @@ -56,6 +56,7 @@ static udt_VariableType vt_Boolean = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) BooleanVar_SetValue, diff --git a/Connection.c b/Connection.c index d582fa7..0adeb57 100644 --- a/Connection.c +++ b/Connection.c @@ -64,6 +64,7 @@ static PyObject *Connection_GetMaxBytesPerCharacter(udt_Connection*, void*); static PyObject *Connection_ContextManagerEnter(udt_Connection*, PyObject*); static PyObject *Connection_ContextManagerExit(udt_Connection*, PyObject*); static PyObject *Connection_ChangePasswordExternal(udt_Connection*, PyObject*); +static PyObject *Connection_GetType(udt_Connection*, PyObject*); static PyObject *Connection_GetStmtCacheSize(udt_Connection*, void*); static int Connection_SetStmtCacheSize(udt_Connection*, PyObject*, void*); #if ORACLE_VERSION_HEX >= ORACLE_VERSION(10, 2) @@ -112,6 +113,7 @@ static PyMethodDef g_ConnectionMethods[] = { #endif { "changepassword", (PyCFunction) Connection_ChangePasswordExternal, METH_VARARGS }, + { "gettype", (PyCFunction) Connection_GetType, METH_VARARGS }, { NULL } }; @@ -1081,6 +1083,24 @@ static int Connection_SetStmtCacheSize( } +//----------------------------------------------------------------------------- +// Connection_GetType() +// Return a type object given its name. +//----------------------------------------------------------------------------- +static PyObject *Connection_GetType( + udt_Connection *self, // connection + PyObject *args) // arguments +{ + PyObject *nameObj = NULL; + + // parse the arguments + if (!PyArg_ParseTuple(args, "O", &nameObj)) + return NULL; + + return (PyObject*) ObjectType_NewByName(self, nameObj); +} + + //----------------------------------------------------------------------------- // Connection_GetVersion() // Retrieve the version of the database and return it. Note that this diff --git a/CursorVar.c b/CursorVar.c index dc83fce..e1abb81 100644 --- a/CursorVar.c +++ b/CursorVar.c @@ -59,6 +59,7 @@ static udt_VariableType vt_Cursor = { (FinalizeProc) CursorVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) CursorVar_SetValue, diff --git a/DateTimeVar.c b/DateTimeVar.c index a1bf6b2..4273021 100644 --- a/DateTimeVar.c +++ b/DateTimeVar.c @@ -55,6 +55,7 @@ static udt_VariableType vt_DateTime = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) DateTimeVar_SetValue, @@ -76,6 +77,7 @@ static udt_VariableType vt_Date = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) DateTimeVar_SetValue, @@ -101,31 +103,7 @@ static int DateTimeVar_SetValue( unsigned pos, // array position to set PyObject *value) // value to set { - ub1 month, day, hour, minute, second; - short year; - - if (PyDateTime_Check(value)) { - year = (short) PyDateTime_GET_YEAR(value); - month = PyDateTime_GET_MONTH(value); - day = PyDateTime_GET_DAY(value); - hour = PyDateTime_DATE_GET_HOUR(value); - minute = PyDateTime_DATE_GET_MINUTE(value); - second = PyDateTime_DATE_GET_SECOND(value); - } else if (PyDate_Check(value)) { - year = (short) PyDateTime_GET_YEAR(value); - month = PyDateTime_GET_MONTH(value); - day = PyDateTime_GET_DAY(value); - hour = minute = second = 0; - } else { - PyErr_SetString(PyExc_TypeError, "expecting date data"); - return -1; - } - - // store a copy of the value - OCIDateSetDate(&var->data[pos], year, month, day); - OCIDateSetTime(&var->data[pos], hour, minute, second); - - return 0; + return PythonDateToOracleDate(value, &var->data[pos]); } diff --git a/Environment.c b/Environment.c index 92406a5..9129177 100644 --- a/Environment.c +++ b/Environment.c @@ -18,6 +18,8 @@ typedef struct { udt_Buffer numberToStringFormatBuffer; udt_Buffer numberFromStringFormatBuffer; udt_Buffer nlsNumericCharactersBuffer; + OCIString *tempStringValue; + OCIDateTime *tempTimestampValue; } udt_Environment; //----------------------------------------------------------------------------- @@ -72,6 +74,7 @@ static udt_Environment *Environment_New( env = (udt_Environment*) g_EnvironmentType.tp_alloc(&g_EnvironmentType, 0); if (!env) return NULL; + env->handle = handle; env->fixedWidth = 1; env->maxBytesPerCharacter = 1; cxBuffer_Init(&env->numberToStringFormatBuffer); @@ -94,7 +97,14 @@ static udt_Environment *Environment_New( return NULL; } - env->handle = handle; + // create the temp datetime handle (used for converting timestamps in + // objects) + status = OCIDescriptorAlloc(handle, (dvoid**) &env->tempTimestampValue, + OCI_DTYPE_TIMESTAMP, 0, 0); + if (Environment_CheckForError(env, status, + "Environment_New(): create timestamp descriptor") < 0) + return NULL; + return env; } diff --git a/IntervalVar.c b/IntervalVar.c index f29381f..d7565cd 100644 --- a/IntervalVar.c +++ b/IntervalVar.c @@ -57,6 +57,7 @@ static udt_VariableType vt_Interval = { (FinalizeProc) IntervalVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) IntervalVar_SetValue, diff --git a/LobVar.c b/LobVar.c index 111d8c2..01ff626 100644 --- a/LobVar.c +++ b/LobVar.c @@ -134,6 +134,7 @@ static udt_VariableType vt_CLOB = { (FinalizeProc) LobVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) LobVar_PreFetch, (IsNullProc) NULL, (SetValueProc) LobVar_SetValue, @@ -155,6 +156,7 @@ static udt_VariableType vt_NCLOB = { (FinalizeProc) LobVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) LobVar_PreFetch, (IsNullProc) NULL, (SetValueProc) LobVar_SetValue, @@ -176,6 +178,7 @@ static udt_VariableType vt_BLOB = { (FinalizeProc) LobVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) LobVar_PreFetch, (IsNullProc) NULL, (SetValueProc) LobVar_SetValue, @@ -197,6 +200,7 @@ static udt_VariableType vt_BFILE = { (FinalizeProc) LobVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) LobVar_PreFetch, (IsNullProc) NULL, (SetValueProc) LobVar_SetValue, diff --git a/LongVar.c b/LongVar.c index 12b82ec..a70d98e 100644 --- a/LongVar.c +++ b/LongVar.c @@ -106,6 +106,7 @@ static udt_VariableType vt_LongString = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) LongVar_SetValue, @@ -127,6 +128,7 @@ static udt_VariableType vt_LongNationalCharString = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) LongVar_SetValue, @@ -148,6 +150,7 @@ static udt_VariableType vt_LongBinary = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) LongVar_SetValue, diff --git a/NumberVar.c b/NumberVar.c index c99bc5d..0bf5804 100644 --- a/NumberVar.c +++ b/NumberVar.c @@ -122,6 +122,7 @@ static udt_VariableType vt_Float = { (FinalizeProc) NULL, (PreDefineProc) NumberVar_PreDefine, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NumberVar_SetValue, @@ -143,6 +144,7 @@ static udt_VariableType vt_NativeFloat = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NativeFloatVar_SetValue, @@ -164,6 +166,7 @@ static udt_VariableType vt_NativeInteger = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NativeIntVar_SetValue, @@ -185,6 +188,7 @@ static udt_VariableType vt_Integer = { (FinalizeProc) NULL, (PreDefineProc) NumberVar_PreDefine, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NumberVar_SetValue, @@ -206,6 +210,7 @@ static udt_VariableType vt_LongInteger = { (FinalizeProc) NULL, (PreDefineProc) NumberVar_PreDefine, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NumberVar_SetValue, @@ -227,6 +232,7 @@ static udt_VariableType vt_NumberAsString = { (FinalizeProc) NULL, (PreDefineProc) NumberVar_PreDefine, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NumberVar_SetValue, @@ -249,6 +255,7 @@ static udt_VariableType vt_Boolean = { (FinalizeProc) NULL, (PreDefineProc) NumberVar_PreDefine, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) NumberVar_SetValue, @@ -306,232 +313,6 @@ static int NumberVar_PreDefine( } -//----------------------------------------------------------------------------- -// NumberVar_SetValueFromBoolean() -// Set the value of the variable from a Python boolean. -//----------------------------------------------------------------------------- -static int NumberVar_SetValueFromBoolean( - udt_NumberVar *var, // variable to set value for - unsigned pos, // array position to set - PyObject *value) // value to set -{ - long integerValue; - sword status; - - integerValue = (value == Py_True); - status = OCINumberFromInt(var->environment->errorHandle, &integerValue, - sizeof(long), OCI_NUMBER_SIGNED, &var->data[pos]); - return Environment_CheckForError(var->environment, status, - "NumberVar_SetValueFromBoolean()"); -} - - -#if PY_MAJOR_VERSION < 3 -//----------------------------------------------------------------------------- -// NumberVar_SetValueFromInteger() -// Set the value of the variable from a Python integer. -//----------------------------------------------------------------------------- -static int NumberVar_SetValueFromInteger( - udt_NumberVar *var, // variable to set value for - unsigned pos, // array position to set - PyObject *value) // value to set -{ - long integerValue; - sword status; - - integerValue = PyInt_AS_LONG(value); - status = OCINumberFromInt(var->environment->errorHandle, &integerValue, - sizeof(long), OCI_NUMBER_SIGNED, &var->data[pos]); - return Environment_CheckForError(var->environment, status, - "NumberVar_SetValueFromInteger()"); -} -#endif - - -//----------------------------------------------------------------------------- -// NumberVar_SetValueFromFloat() -// Set the value of the variable from a Python float. -//----------------------------------------------------------------------------- -static int NumberVar_SetValueFromFloat( - udt_NumberVar *var, // variable to set value for - unsigned pos, // array position to set - PyObject *value) // value to set -{ - double doubleValue; - sword status; - - doubleValue = PyFloat_AS_DOUBLE(value); - status = OCINumberFromReal(var->environment->errorHandle, &doubleValue, - sizeof(double), &var->data[pos]); - return Environment_CheckForError(var->environment, status, - "NumberVar_SetValueFromFloat()"); -} - - -//----------------------------------------------------------------------------- -// NumberVar_SetValueFromLong() -// Set the value of the variable from a Python long. -//----------------------------------------------------------------------------- -static int NumberVar_SetValueFromLong( - udt_NumberVar *var, // variable to set value for - unsigned pos, // array position to set - PyObject *value) // value to set -{ - udt_Buffer textBuffer; - PyObject *textValue; - sword status; - - textValue = PyObject_Str(value); - if (!textValue) - return -1; - if (cxBuffer_FromObject(&textBuffer, textValue, - var->environment->encoding) < 0) - return -1; - status = OCINumberFromText(var->environment->errorHandle, - (text*) textBuffer.ptr, textBuffer.size, - (text*) var->environment->numberFromStringFormatBuffer.ptr, - var->environment->numberFromStringFormatBuffer.size, NULL, 0, - &var->data[pos]); - cxBuffer_Clear(&textBuffer); - Py_DECREF(textValue); - return Environment_CheckForError(var->environment, status, - "NumberVar_SetValueFromLong()"); -} - - -//----------------------------------------------------------------------------- -// NumberVar_GetFormatAndTextFromDecimal() -// Return the number format and text to use for the Decimal object. -//----------------------------------------------------------------------------- -static int NumberVar_GetFormatAndTextFromDecimal( - 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; -} - - -//----------------------------------------------------------------------------- -// NumberVar_SetValueFromDecimal() -// Set the value of the variable from a Python decimal.Decimal object. -//----------------------------------------------------------------------------- -static int NumberVar_SetValueFromDecimal( - udt_NumberVar *var, // variable to set value for - unsigned pos, // array position to set - PyObject *value) // value to set -{ - PyObject *textValue, *format, *tupleValue; - udt_Buffer textBuffer, formatBuffer; - sword status; - - tupleValue = PyObject_CallMethod(value, "as_tuple", NULL); - if (!tupleValue) - return -1; - if (NumberVar_GetFormatAndTextFromDecimal(tupleValue, &textValue, - &format) < 0) { - Py_DECREF(tupleValue); - return -1; - } - Py_DECREF(tupleValue); - if (cxBuffer_FromObject(&textBuffer, textValue, - var->environment->encoding) < 0) - return -1; - if (cxBuffer_FromObject(&formatBuffer, format, - var->environment->encoding) < 0) { - cxBuffer_Clear(&textBuffer); - return -1; - } - status = OCINumberFromText(var->environment->errorHandle, - (text*) textBuffer.ptr, textBuffer.size, (text*) formatBuffer.ptr, - formatBuffer.size, - var->environment->nlsNumericCharactersBuffer.ptr, - var->environment->nlsNumericCharactersBuffer.size, - &var->data[pos]); - cxBuffer_Clear(&textBuffer); - cxBuffer_Clear(&formatBuffer); - Py_DECREF(textValue); - Py_DECREF(format); - return Environment_CheckForError(var->environment, status, - "NumberVar_SetValueFromDecimal()"); -} - - //----------------------------------------------------------------------------- // NumberVar_SetValue() // Set the value of the variable. @@ -541,20 +322,8 @@ static int NumberVar_SetValue( unsigned pos, // array position to set PyObject *value) // value to set { -#if PY_MAJOR_VERSION < 3 - if (PyInt_Check(value)) - return NumberVar_SetValueFromInteger(var, pos, value); -#endif - if (PyBool_Check(value)) - return NumberVar_SetValueFromBoolean(var, pos, value); - if (PyLong_Check(value)) - return NumberVar_SetValueFromLong(var, pos, value); - if (PyFloat_Check(value)) - return NumberVar_SetValueFromFloat(var, pos, value); - if (Py_TYPE(value) == g_DecimalType) - return NumberVar_SetValueFromDecimal(var, pos, value); - PyErr_SetString(PyExc_TypeError, "expecting numeric data"); - return -1; + return PythonNumberToOracleNumber(var->environment, value, + &var->data[pos]); } diff --git a/Object.c b/Object.c index f712ee4..1e1a7c8 100644 --- a/Object.c +++ b/Object.c @@ -20,6 +20,7 @@ typedef struct { //----------------------------------------------------------------------------- static void Object_Free(udt_Object*); static PyObject *Object_GetAttr(udt_Object*, PyObject*); +static int Object_SetAttr(udt_Object*, PyObject*, PyObject*); static PyObject *Object_ConvertToPython(udt_Environment*, OCITypeCode, dvoid*, dvoid*, udt_ObjectType*); @@ -52,7 +53,7 @@ static PyTypeObject g_ObjectType = { 0, // tp_call 0, // tp_str (getattrofunc) Object_GetAttr, // tp_getattro - 0, // tp_setattro + (setattrofunc) Object_SetAttr, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT, // tp_flags 0, // tp_doc @@ -99,9 +100,9 @@ static void Object_Free( udt_Object *self) // variable to free { if (self->isIndependent) - OCIObjectFree(self->objectType->environment->handle, - self->objectType->environment->errorHandle, - self->instance, OCI_OBJECTFREE_FORCE); + OCIObjectFree(self->objectType->connection->environment->handle, + self->objectType->connection->environment->errorHandle, + self->instance, OCI_DEFAULT); Py_CLEAR(self->objectType); Py_TYPE(self)->tp_free((PyObject*) self); } @@ -243,21 +244,23 @@ static PyObject *Object_GetAttributeValue( { dvoid *valueIndicator, *value; OCIInd scalarValueIndicator; + udt_Connection *connection; udt_Buffer buffer; sword status; OCIType *tdo; // get the value for the attribute + connection = self->objectType->connection; if (cxBuffer_FromObject(&buffer, attribute->name, - self->objectType->environment->encoding) < 0) + connection->environment->encoding) < 0) return NULL; - status = OCIObjectGetAttr(self->objectType->environment->handle, - self->objectType->environment->errorHandle, self->instance, + status = OCIObjectGetAttr(connection->environment->handle, + connection->environment->errorHandle, self->instance, self->indicator, self->objectType->tdo, (const OraText**) &buffer.ptr, (ub4*) &buffer.size, 1, 0, 0, &scalarValueIndicator, &valueIndicator, &value, &tdo); cxBuffer_Clear(&buffer); - if (Environment_CheckForError(self->objectType->environment, status, + if (Environment_CheckForError(connection->environment, status, "Object_GetAttributeValue(): getting value") < 0) return NULL; @@ -265,14 +268,111 @@ static PyObject *Object_GetAttributeValue( if (!valueIndicator) valueIndicator = &scalarValueIndicator; - return Object_ConvertToPython(self->objectType->environment, + return Object_ConvertToPython(connection->environment, attribute->typeCode, value, valueIndicator, attribute->subType); } +//----------------------------------------------------------------------------- +// Object_SetAttributeValue() +// Set an attribute on the object. +//----------------------------------------------------------------------------- +static int Object_SetAttributeValue( + udt_Object *self, // object + udt_ObjectAttribute *attribute, // attribute to set + PyObject *value) // value to set +{ + dvoid *ociValueIndicator, *ociValue; + OCIInd ociScalarValueIndicator; + udt_Connection *connection; + OCINumber numericValue; + udt_Buffer buffer; + OCIDate dateValue; + sword status; + + // initialization + ociValue = NULL; + ociValueIndicator = NULL; + connection = self->objectType->connection; + + // None is treated as null + if (value == Py_None) { + ociScalarValueIndicator = OCI_IND_NULL; + + // all other values need to be converted + } else { + + ociScalarValueIndicator = OCI_IND_NOTNULL; + switch (attribute->typeCode) { + case OCI_TYPECODE_CHAR: + case OCI_TYPECODE_VARCHAR: + case OCI_TYPECODE_VARCHAR2: + if (cxBuffer_FromObject(&buffer, value, + connection->environment->encoding) < 0) + return -1; + status = OCIStringAssignText(connection->environment->handle, + connection->environment->errorHandle, buffer.ptr, + buffer.size, + &connection->environment->tempStringValue); + cxBuffer_Clear(&buffer); + if (Environment_CheckForError(connection->environment, status, + "Object_SetAttributeValue(): assigning string") < 0) + return -1; + ociValue = connection->environment->tempStringValue; + break; + case OCI_TYPECODE_NUMBER: + ociValue = &numericValue; + if (PythonNumberToOracleNumber(connection->environment, value, + ociValue) < 0) + return -1; + break; + case OCI_TYPECODE_DATE: + ociValue = &dateValue; + if (PythonDateToOracleDate(value, ociValue) < 0) + return -1; + break; + case OCI_TYPECODE_TIMESTAMP: + ociValue = connection->environment->tempTimestampValue; + if (PythonDateToOracleTimestamp(connection->environment, value, + ociValue) < 0) + return -1; + break; + case OCI_TYPECODE_OBJECT: + break; + case OCI_TYPECODE_NAMEDCOLLECTION: + break; + }; + + if (!ociValue) { + PyErr_Format(g_NotSupportedErrorException, + "Object_SetAttributeValue(): unhandled data type %d", + attribute->typeCode); + return -1; + } + + } + + // set the value for the attribute + if (cxBuffer_FromObject(&buffer, attribute->name, + connection->environment->encoding) < 0) + return -1; + status = OCIObjectSetAttr(connection->environment->handle, + connection->environment->errorHandle, self->instance, + self->indicator, self->objectType->tdo, + (const OraText**) &buffer.ptr, (ub4*) &buffer.size, 1, 0, 0, + ociScalarValueIndicator, ociValueIndicator, ociValue); + cxBuffer_Clear(&buffer); + if (Environment_CheckForError(connection->environment, status, + "Object_SetAttributeValue(): setting value") < 0) + return -1; + + return 0; +} + + //----------------------------------------------------------------------------- // Object_GetAttr() -// Retrieve an attribute on object. +// Retrieve an attribute on an object. //----------------------------------------------------------------------------- static PyObject *Object_GetAttr( udt_Object *self, // object @@ -288,3 +388,23 @@ static PyObject *Object_GetAttr( return PyObject_GenericGetAttr( (PyObject*) self, nameObject); } + +//----------------------------------------------------------------------------- +// Object_SetAttr() +// Set an attribute on an object. +//----------------------------------------------------------------------------- +static int Object_SetAttr( + udt_Object *self, // object + PyObject *nameObject, // name of attribute + PyObject *value) // value to set +{ + udt_ObjectAttribute *attribute; + + attribute = (udt_ObjectAttribute*) + PyDict_GetItem(self->objectType->attributesByName, nameObject); + if (attribute) + return Object_SetAttributeValue(self, attribute, value); + + return PyObject_GenericSetAttr( (PyObject*) self, nameObject, value); +} + diff --git a/ObjectType.c b/ObjectType.c index 4699306..40f42f9 100644 --- a/ObjectType.c +++ b/ObjectType.c @@ -8,7 +8,7 @@ //----------------------------------------------------------------------------- typedef struct { PyObject_HEAD - udt_Environment *environment; + udt_Connection *connection; OCIType *tdo; PyObject *schema; PyObject *name; @@ -27,6 +27,7 @@ typedef struct { udt_ObjectType *subType; } udt_ObjectAttribute; +#include "Object.c" //----------------------------------------------------------------------------- // Declaration of type variable functions. @@ -34,11 +35,21 @@ typedef struct { static udt_ObjectType *ObjectType_New(udt_Connection*, OCIParam*, ub4); static void ObjectType_Free(udt_ObjectType*); static PyObject *ObjectType_Repr(udt_ObjectType*); +static PyObject *ObjectType_NewObject(udt_ObjectType*, PyObject*); static udt_ObjectAttribute *ObjectAttribute_New(udt_Connection*, OCIParam*); static void ObjectAttribute_Free(udt_ObjectAttribute*); static PyObject *ObjectAttribute_Repr(udt_ObjectAttribute*); +//----------------------------------------------------------------------------- +// declaration of methods for Python type "ObjectType" +//----------------------------------------------------------------------------- +static PyMethodDef g_ObjectTypeMethods[] = { + { "newobject", (PyCFunction) ObjectType_NewObject, METH_NOARGS }, + { NULL, NULL } +}; + + //----------------------------------------------------------------------------- // declaration of members for Python type "ObjectType" //----------------------------------------------------------------------------- @@ -90,7 +101,7 @@ static PyTypeObject g_ObjectTypeType = { 0, // tp_weaklistoffset 0, // tp_iter 0, // tp_iternext - 0, // tp_methods + g_ObjectTypeMethods, // tp_methods g_ObjectTypeMembers, // tp_members 0, // tp_getset 0, // tp_base @@ -158,7 +169,6 @@ static PyTypeObject g_ObjectAttributeType = { //----------------------------------------------------------------------------- static int ObjectType_Describe( udt_ObjectType *self, // type to populate - udt_Connection *connection, // connection for type information OCIDescribe *describeHandle) // describe handle { OCIParam *topLevelParam, *attributeListParam, *attributeParam; @@ -170,24 +180,24 @@ static int ObjectType_Describe( int i; // describe the type - status = OCIDescribeAny(connection->handle, self->environment->errorHandle, - (dvoid*) self->tdo, 0, OCI_OTYPE_PTR, OCI_DEFAULT, OCI_PTYPE_TYPE, - describeHandle); - if (Environment_CheckForError(self->environment, status, + status = OCIDescribeAny(self->connection->handle, + self->connection->environment->errorHandle, (dvoid*) self->tdo, 0, + OCI_OTYPE_PTR, OCI_DEFAULT, OCI_PTYPE_TYPE, describeHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): describe type") < 0) return -1; // get top level parameter descriptor status = OCIAttrGet(describeHandle, OCI_HTYPE_DESCRIBE, &topLevelParam, 0, - OCI_ATTR_PARAM, self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + OCI_ATTR_PARAM, self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get top level parameter descriptor") < 0) return -1; // determine type of type status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, &typeCode, 0, - OCI_ATTR_TYPECODE, self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + OCI_ATTR_TYPECODE, self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get type code") < 0) return -1; @@ -198,31 +208,31 @@ static int ObjectType_Describe( // determine type of collection status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, &self->collectionTypeCode, 0, OCI_ATTR_COLLECTION_TYPECODE, - self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get collection type code") < 0) return -1; // acquire collection parameter descriptor status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, &collectionParam, 0, OCI_ATTR_COLLECTION_ELEMENT, - self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get collection descriptor") < 0) return -1; // determine type of element status = OCIAttrGet(collectionParam, OCI_DTYPE_PARAM, &self->elementTypeCode, 0, OCI_ATTR_TYPECODE, - self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get element type code") < 0) return -1; // if element type is an object type get its type if (self->elementTypeCode == OCI_TYPECODE_OBJECT) { self->elementType = (PyObject*) - ObjectType_New(connection, collectionParam, + ObjectType_New(self->connection, collectionParam, OCI_ATTR_TYPE_NAME); if (!self->elementType) return -1; @@ -233,8 +243,8 @@ static int ObjectType_Describe( // determine the number of attributes status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, (dvoid*) &numAttributes, 0, OCI_ATTR_NUM_TYPE_ATTRS, - self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get number of attributes") < 0) return -1; @@ -249,20 +259,20 @@ static int ObjectType_Describe( // acquire the list parameter descriptor status = OCIAttrGet(topLevelParam, OCI_DTYPE_PARAM, (dvoid*) &attributeListParam, 0, OCI_ATTR_LIST_TYPE_ATTRS, - self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get list parameter descriptor") < 0) return -1; // create attribute information for each attribute for (i = 0; i < numAttributes; i++) { status = OCIParamGet(attributeListParam, OCI_DTYPE_PARAM, - self->environment->errorHandle, (dvoid**) &attributeParam, - (ub4) i + 1); - if (Environment_CheckForError(self->environment, status, + self->connection->environment->errorHandle, + (dvoid**) &attributeParam, (ub4) i + 1); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Describe(): get attribute param descriptor") < 0) return -1; - attribute = ObjectAttribute_New(connection, attributeParam); + attribute = ObjectAttribute_New(self->connection, attributeParam); if (!attribute) return -1; PyList_SET_ITEM(self->attributes, i, (PyObject*) attribute); @@ -281,7 +291,6 @@ static int ObjectType_Describe( //----------------------------------------------------------------------------- static int ObjectType_Initialize( udt_ObjectType *self, // type to initialize - udt_Connection *connection, // connection for type information OCIParam *param, // parameter descriptor ub4 nameAttribute) // value for the name attribute { @@ -293,53 +302,54 @@ static int ObjectType_Initialize( // determine the schema of the type status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &name, &size, - OCI_ATTR_SCHEMA_NAME, self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + OCI_ATTR_SCHEMA_NAME, self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Initialize(): get schema name") < 0) return -1; self->schema = cxString_FromEncodedString(name, size, - self->environment->encoding); + self->connection->environment->encoding); if (!self->schema) return -1; // determine the name of the type status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &name, &size, - nameAttribute, self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + nameAttribute, self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Initialize(): get name") < 0) return -1; self->name = cxString_FromEncodedString(name, size, - self->environment->encoding); + self->connection->environment->encoding); if (!self->name) return -1; // retrieve TDO of the parameter status = OCIAttrGet(param, OCI_HTYPE_DESCRIBE, (dvoid*) &tdoReference, 0, - OCI_ATTR_REF_TDO, self->environment->errorHandle); - if (Environment_CheckForError(self->environment, status, + OCI_ATTR_REF_TDO, self->connection->environment->errorHandle); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Initialize(): get TDO reference") < 0) return -1; - status = OCIObjectPin(self->environment->handle, - self->environment->errorHandle, tdoReference, NULL, OCI_PIN_ANY, - OCI_DURATION_SESSION, OCI_LOCK_NONE, (dvoid**) &self->tdo); - if (Environment_CheckForError(self->environment, status, + status = OCIObjectPin(self->connection->environment->handle, + self->connection->environment->errorHandle, tdoReference, NULL, + OCI_PIN_ANY, OCI_DURATION_SESSION, OCI_LOCK_NONE, + (dvoid**) &self->tdo); + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Initialize(): pin TDO reference") < 0) return -1; // acquire a describe handle - status = OCIHandleAlloc(self->environment->handle, + status = OCIHandleAlloc(self->connection->environment->handle, (dvoid**) &describeHandle, OCI_HTYPE_DESCRIBE, 0, 0); - if (Environment_CheckForError(self->environment, status, + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Initialize(): allocate describe handle") < 0) return -1; // describe the type - if (ObjectType_Describe(self, connection, describeHandle) < 0) + if (ObjectType_Describe(self, describeHandle) < 0) return -1; // free the describe handle status = OCIHandleFree(describeHandle, OCI_HTYPE_DESCRIBE); - if (Environment_CheckForError(self->environment, status, + if (Environment_CheckForError(self->connection->environment, status, "ObjectType_Initialize(): free describe handle") < 0) return -1; @@ -361,9 +371,9 @@ static udt_ObjectType *ObjectType_New( self = (udt_ObjectType*) g_ObjectTypeType.tp_alloc(&g_ObjectTypeType, 0); if (!self) return NULL; - Py_INCREF(connection->environment); - self->environment = connection->environment; - if (ObjectType_Initialize(self, connection, param, nameAttribute) < 0) { + Py_INCREF(connection); + self->connection = connection; + if (ObjectType_Initialize(self, param, nameAttribute) < 0) { Py_DECREF(self); return NULL; } @@ -443,9 +453,9 @@ static void ObjectType_Free( udt_ObjectType *self) // object type to free { if (self->tdo) - OCIObjectUnpin(self->environment->handle, - self->environment->errorHandle, self->tdo); - Py_CLEAR(self->environment); + OCIObjectUnpin(self->connection->environment->handle, + self->connection->environment->errorHandle, self->tdo); + Py_CLEAR(self->connection); Py_CLEAR(self->schema); Py_CLEAR(self->name); Py_CLEAR(self->attributes); @@ -486,6 +496,38 @@ static PyObject *ObjectType_Repr( } +//----------------------------------------------------------------------------- +// ObjectType_NewObject() +// Factory function for creating objects of the type which can be bound. +//----------------------------------------------------------------------------- +static PyObject *ObjectType_NewObject( + udt_ObjectType *self, // object type to return the string for + PyObject *args) // arguments (none, ignored) +{ + dvoid *instance, *indicator; + sword status; + + // create the object instance + status = OCIObjectNew(self->connection->environment->handle, + self->connection->environment->errorHandle, + self->connection->handle, OCI_TYPECODE_OBJECT, self->tdo, NULL, + OCI_DURATION_SESSION, TRUE, &instance); + if (Environment_CheckForError(self->connection->environment, status, + "ObjectType_NewObject(): create object instance") < 0) + return NULL; + + // get the null indicator structure + status = OCIObjectGetInd(self->connection->environment->handle, + self->connection->environment->errorHandle, instance, &indicator); + if (Environment_CheckForError(self->connection->environment, status, + "ObjectType_NewObject(): get indicator structure") < 0) + return NULL; + + return Object_New(self, instance, indicator, 1); +} + + +static PyObject *ObjectType_NewObject(udt_ObjectType*, PyObject*); //----------------------------------------------------------------------------- // ObjectAttribute_Initialize() // Initialize the new object attribute. diff --git a/ObjectVar.c b/ObjectVar.c index a1ba50f..4e6159a 100644 --- a/ObjectVar.c +++ b/ObjectVar.c @@ -4,7 +4,6 @@ //----------------------------------------------------------------------------- #include "ObjectType.c" -#include "Object.c" //----------------------------------------------------------------------------- // Object type @@ -23,9 +22,11 @@ typedef struct { //----------------------------------------------------------------------------- static int ObjectVar_Initialize(udt_ObjectVar*, udt_Cursor*); static void ObjectVar_Finalize(udt_ObjectVar*); +static int ObjectVar_SetValue(udt_ObjectVar*, unsigned, PyObject*); static PyObject *ObjectVar_GetValue(udt_ObjectVar*, unsigned); static int ObjectVar_PreDefine(udt_ObjectVar*, OCIParam*); static int ObjectVar_PostDefine(udt_ObjectVar*); +static int ObjectVar_PostBind(udt_ObjectVar*); static int ObjectVar_PreFetch(udt_ObjectVar*); static int ObjectVar_IsNull(udt_ObjectVar*, unsigned); @@ -82,9 +83,10 @@ static udt_VariableType vt_Object = { (FinalizeProc) ObjectVar_Finalize, (PreDefineProc) ObjectVar_PreDefine, (PostDefineProc) ObjectVar_PostDefine, + (PostBindProc) ObjectVar_PostBind, (PreFetchProc) ObjectVar_PreFetch, (IsNullProc) ObjectVar_IsNull, - (SetValueProc) NULL, + (SetValueProc) ObjectVar_SetValue, (GetValueProc) ObjectVar_GetValue, (GetBufferSizeProc) NULL, &g_ObjectVarType, // Python type @@ -142,8 +144,9 @@ static void ObjectVar_Finalize( ub4 i; for (i = 0; i < self->allocatedElements; i++) { - Py_CLEAR(self->objects[i]); - if (self->data[i]) + if (self->objects[i]) + Py_CLEAR(self->objects[i]); + else if (self->data[i]) OCIObjectFree(self->environment->handle, self->environment->errorHandle, self->data[i], OCI_DEFAULT); @@ -190,6 +193,22 @@ static int ObjectVar_PostDefine( } +//----------------------------------------------------------------------------- +// ObjectVar_PostBind() +// Performs additional steps required for binding objects. +//----------------------------------------------------------------------------- +static int ObjectVar_PostBind( + udt_ObjectVar *self) // variable to set up +{ + sword status; + + status = OCIBindObject(self->bindHandle, self->environment->errorHandle, + self->objectType->tdo, self->data, 0, self->objectIndicator, 0); + return Environment_CheckForError(self->environment, status, + "ObjectVar_PostBind(): bind object"); +} + + //----------------------------------------------------------------------------- // ObjectVar_PreFetch() // Free objects prior to next fetch. @@ -224,6 +243,47 @@ static int ObjectVar_IsNull( } +//----------------------------------------------------------------------------- +// ObjectVar_SetValue() +// Set the value of the variable. +//----------------------------------------------------------------------------- +static int ObjectVar_SetValue( + udt_ObjectVar *self, // variable to determine value for + unsigned pos, // array position + PyObject *value) // value to set +{ + udt_Object *object; + + // only cx_Oracle.Object values are permitted and the types must match + // if the variable doesn't have a type yet, assign it + if (Py_TYPE(value) != &g_ObjectType) { + PyErr_SetString(PyExc_TypeError, "expecting cx_Oracle.Object"); + return -1; + } + object = (udt_Object*) value; + if (!self->objectType) { + Py_INCREF(object->objectType); + self->objectType = object->objectType; + } else if (object->objectType != self->objectType) { + PyErr_SetString(PyExc_TypeError, + "expecting same type as the variable itself"); + return -1; + } + + // eliminate prior value, if needed + if (self->objects[pos]) + Py_CLEAR(self->objects[pos]); + else OCIObjectFree(self->environment->handle, + self->environment->errorHandle, self->data[pos], OCI_DEFAULT); + + // set new value + Py_INCREF(value); + self->objects[pos] = value; + self->data[pos] = object->instance; + self->objectIndicator[pos] = object->indicator; + return 0; +} + //----------------------------------------------------------------------------- // ObjectVar_GetValue() // Returns the value stored at the given array position. @@ -245,8 +305,6 @@ static PyObject *ObjectVar_GetValue( if (!obj) return NULL; self->objects[pos] = obj; - self->data[pos] = NULL; - self->objectIndicator[pos] = NULL; } Py_INCREF(self->objects[pos]); diff --git a/StringVar.c b/StringVar.c index 7b79ce9..24eedfc 100644 --- a/StringVar.c +++ b/StringVar.c @@ -182,6 +182,7 @@ static udt_VariableType vt_String = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) StringVar_SetValue, @@ -203,6 +204,7 @@ static udt_VariableType vt_NationalCharString = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) StringVar_PostDefine, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) StringVar_SetValue, @@ -224,6 +226,7 @@ static udt_VariableType vt_FixedChar = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) StringVar_SetValue, @@ -245,6 +248,7 @@ static udt_VariableType vt_FixedNationalChar = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) StringVar_PostDefine, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) StringVar_SetValue, @@ -266,6 +270,7 @@ static udt_VariableType vt_Rowid = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) StringVar_SetValue, @@ -287,6 +292,7 @@ static udt_VariableType vt_Binary = { (FinalizeProc) NULL, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) StringVar_SetValue, diff --git a/TimestampVar.c b/TimestampVar.c index de625ec..50d4fcf 100644 --- a/TimestampVar.c +++ b/TimestampVar.c @@ -57,6 +57,7 @@ static udt_VariableType vt_Timestamp = { (FinalizeProc) TimestampVar_Finalize, (PreDefineProc) NULL, (PostDefineProc) NULL, + (PostBindProc) NULL, (PreFetchProc) NULL, (IsNullProc) NULL, (SetValueProc) TimestampVar_SetValue, @@ -122,39 +123,8 @@ static int TimestampVar_SetValue( unsigned pos, // array position to set PyObject *value) // value to set { - sword status; - uword valid; - - // make sure a timestamp is being bound - if (!PyDateTime_Check(value)) { - PyErr_SetString(PyExc_TypeError, "expecting timestamp data"); - return -1; - } - - // store a copy of the value - status = OCIDateTimeConstruct(var->environment->handle, - var->environment->errorHandle, var->data[pos], - (sb2) PyDateTime_GET_YEAR(value), - PyDateTime_GET_MONTH(value), - PyDateTime_GET_DAY(value), - PyDateTime_DATE_GET_HOUR(value), - PyDateTime_DATE_GET_MINUTE(value), - PyDateTime_DATE_GET_SECOND(value), - PyDateTime_DATE_GET_MICROSECOND(value) * 1000, NULL, 0); - if (Environment_CheckForError(var->environment, status, - "TimestampVar_SetValue(): create structure") < 0) - return -1; - status = OCIDateTimeCheck(var->environment->handle, - var->environment->errorHandle, var->data[pos], &valid); - if (Environment_CheckForError(var->environment, status, - "TimestampVar_SetValue()") < 0) - return -1; - if (valid != 0) { - PyErr_SetString(g_DataErrorException, "invalid date"); - return -1; - } - - return 0; + return PythonDateToOracleTimestamp(var->environment, value, + var->data[pos]); } diff --git a/Transforms.c b/Transforms.c index 8410492..2488ceb 100644 --- a/Transforms.c +++ b/Transforms.c @@ -96,3 +96,335 @@ static PyObject *OracleNumberToPythonFloat( return PyFloat_FromDouble(doubleValue); } + +//----------------------------------------------------------------------------- +// 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; +} + diff --git a/Variable.c b/Variable.c index f43f86f..f544940 100644 --- a/Variable.c +++ b/Variable.c @@ -53,6 +53,7 @@ typedef int (*InitializeProc)(udt_Variable*, udt_Cursor*); typedef void (*FinalizeProc)(udt_Variable*); typedef int (*PreDefineProc)(udt_Variable*, OCIParam*); typedef int (*PostDefineProc)(udt_Variable*); +typedef int (*PostBindProc)(udt_Variable*); typedef int (*PreFetchProc)(udt_Variable*); typedef int (*IsNullProc)(udt_Variable*, unsigned); typedef int (*SetValueProc)(udt_Variable*, unsigned, PyObject*); @@ -68,6 +69,7 @@ typedef struct _udt_VariableType { FinalizeProc finalizeProc; PreDefineProc preDefineProc; PostDefineProc postDefineProc; + PostBindProc postBindProc; PreFetchProc preFetchProc; IsNullProc isNullProc; SetValueProc setValueProc; @@ -371,6 +373,7 @@ static int Variable_Check( Py_TYPE(object) == &g_BinaryVarType || Py_TYPE(object) == &g_TimestampVarType || Py_TYPE(object) == &g_IntervalVarType || + Py_TYPE(object) == &g_ObjectVarType || #if ORACLE_VERSION_HEX >= ORACLE_VERSION(12,1) Py_TYPE(object) == &g_BooleanVarType || #endif @@ -461,6 +464,8 @@ static udt_VariableType *Variable_TypeByPythonType( return &vt_NativeInteger; if (type == (PyObject*) &g_ObjectVarType) return &vt_Object; + if (type == (PyObject*) &g_ObjectType) + return &vt_Object; PyErr_SetString(g_NotSupportedErrorException, "Variable_TypeByPythonType(): unhandled data type"); @@ -538,6 +543,8 @@ static udt_VariableType *Variable_TypeByValue( return &vt_DateTime; if (Py_TYPE(value) == g_DecimalType) return &vt_NumberAsString; + if (Py_TYPE(value) == &g_ObjectType) + return &vt_Object; // handle arrays if (PyList_Check(value)) { @@ -1119,6 +1126,12 @@ static int Variable_InternalBind( return -1; } + // call the procedure to set values after define + if (var->type->postBindProc) { + if ((*var->type->postBindProc)(var) < 0) + return -1; + } + return 0; } diff --git a/doc/connection.rst b/doc/connection.rst index 76f71f7..248cc4b 100644 --- a/doc/connection.rst +++ b/doc/connection.rst @@ -169,6 +169,19 @@ Connection Object available in Python 2.x when not built in unicode mode. +.. method:: Connection.gettype(name) + + Return a type object (:ref:`objecttype`) given its name. This can then be + used to create objects which can be bound to cursors created by this + connection. + + .. versionadded:: development + + .. note:: + + This method is an extension to the DB API definition. + + .. attribute:: Connection.inputtypehandler This read-write attribute specifies a method called for each value that is diff --git a/doc/cursor.rst b/doc/cursor.rst index a8574b5..60b7800 100644 --- a/doc/cursor.rst +++ b/doc/cursor.rst @@ -490,9 +490,7 @@ Cursor Object outconverter specify methods used for converting values to/from the database. More information can be found in the section on variable objects. - To create an empty SQL object variable, specify the typename. Additional - support for editing the attributes of this object is not yet available but - will be forthcoming in a future release. + To create an empty SQL object variable, specify the typename. This method was designed for use with PL/SQL in/out variables where the length or type cannot be determined automatically from the Python object diff --git a/doc/index.rst b/doc/index.rst index 16ceb79..d724d0c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -21,6 +21,7 @@ Contents: session_pool.rst subscription.rst lob.rst + objecttype.rst releasenotes.rst license.rst diff --git a/doc/objecttype.rst b/doc/objecttype.rst new file mode 100644 index 0000000..4811784 --- /dev/null +++ b/doc/objecttype.rst @@ -0,0 +1,35 @@ +.. _objecttype: + +******************* +Object Type Objects +******************* + +.. note:: + + This object is an extension to the DB API. It is returned by the + :meth:`~Connection.gettype()` call and is available as the + :data:`~Variable.type` for variables containing Oracle objects. + + +.. attribute:: ObjectType.attributes + + This read-only attribute returns a list of the attributes that make up the + object type. Each attribute has a name attribute on it. + + +.. attribute:: ObjectType.name + + This read-only attribute returns the name of the type. + + +.. method:: ObjectType.newobject() + + Return a new Oracle object of the given type. This object can then be + modified by setting its attributes and then bound to a cursor for + interaction with Oracle. + + +.. attribute:: ObjectType.schema + + This read-only attribute returns the name of the schema that owns the type. + diff --git a/doc/variable.rst b/doc/variable.rst index ca782c5..1ede8a0 100644 --- a/doc/variable.rst +++ b/doc/variable.rst @@ -73,3 +73,10 @@ Variable Objects value is the size in characters. For all others, this is same value as the attribute bufferSize. + +.. attribute:: Variable.type + + This read-only attribute returns the type of the variable for those + variables that bind Oracle objects (it is not present for any other type of + variable). +