diff --git a/Object.c b/Object.c index 23d0d0e..18e4e81 100644 --- a/Object.c +++ b/Object.c @@ -167,6 +167,49 @@ PyObject *Object_New( } +//----------------------------------------------------------------------------- +// Object_Create() +// Create a new object in the OCI. +//----------------------------------------------------------------------------- +static udt_Object *Object_Create( + udt_ObjectType *self) // type of object to create +{ + dvoid *instance; + udt_Object *obj; + sword status; + + // create the object instance + status = OCIObjectNew(self->connection->environment->handle, + self->connection->environment->errorHandle, + self->connection->handle, self->typeCode, self->tdo, NULL, + OCI_DURATION_SESSION, TRUE, &instance); + if (Environment_CheckForError(self->connection->environment, status, + "Object_Create(): create object instance") < 0) + return NULL; + + // create the object + obj = (udt_Object*) Object_New(self, instance, NULL, 1); + if (!obj) { + OCIObjectFree(self->connection->environment->handle, + self->connection->environment->errorHandle, instance, + OCI_DEFAULT); + return NULL; + } + + // get the null indicator structure + status = OCIObjectGetInd(self->connection->environment->handle, + self->connection->environment->errorHandle, instance, + &obj->indicator); + if (Environment_CheckForError(self->connection->environment, status, + "Object_Create(): get indicator structure") < 0) { + Py_DECREF(obj); + return NULL; + } + + return obj; +} + + //----------------------------------------------------------------------------- // Object_Free() // Free an object. @@ -578,6 +621,36 @@ static int Object_InternalAppend( } +//----------------------------------------------------------------------------- +// Object_InternalExtend() +// Extend the collection by appending each of the items in the sequence. +//----------------------------------------------------------------------------- +static int Object_InternalExtend( + udt_Object *self, // object + PyObject *sequence) // sequence to extend collection with +{ + PyObject *fastSequence, *element; + Py_ssize_t size, i; + + // make sure we are dealing with a collection + if (Object_CheckIsCollection(self) < 0) + return -1; + + // append each of the items in the sequence to the collection + fastSequence = PySequence_Fast(sequence, "expecting sequence"); + if (!fastSequence) + return -1; + size = PySequence_Fast_GET_SIZE(fastSequence); + for (i = 0; i < size; i++) { + element = PySequence_Fast_GET_ITEM(fastSequence, i); + if (Object_InternalAppend(self, element) < 0) + return -1; + } + + return 0; +} + + //----------------------------------------------------------------------------- // Object_Append() // Append an item to the collection. @@ -651,7 +724,7 @@ static PyObject *Object_Copy( udt_Object *copiedObject; sword status; - copiedObject = (udt_Object*) ObjectType_NewObject(self->objectType, args); + copiedObject = Object_Create(self->objectType); if (!copiedObject) return NULL; environment = self->objectType->connection->environment; @@ -730,28 +803,12 @@ static PyObject *Object_Extend( udt_Object *self, // object PyObject *args) // arguments { - PyObject *sequence, *fastSequence, *element; - Py_ssize_t size, i; + PyObject *sequence; - // make sure we are dealing with a collection - if (Object_CheckIsCollection(self) < 0) - return NULL; - - // parse arguments if (!PyArg_ParseTuple(args, "O", &sequence)) return NULL; - fastSequence = PySequence_Fast(sequence, "expecting sequence"); - if (!fastSequence) + if (Object_InternalExtend(self, sequence) < 0) return NULL; - - // append each of the items in the sequence to the collection - size = PySequence_Fast_GET_SIZE(fastSequence); - for (i = 0; i < size; i++) { - element = PySequence_Fast_GET_ITEM(fastSequence, i); - if (Object_InternalAppend(self, element) < 0) - return NULL; - } - Py_RETURN_NONE; } diff --git a/ObjectType.c b/ObjectType.c index 1b31c14..97925d3 100644 --- a/ObjectType.c +++ b/ObjectType.c @@ -34,7 +34,7 @@ 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 PyObject *ObjectType_NewObject(udt_ObjectType*, PyObject*, PyObject*); static udt_ObjectAttribute *ObjectAttribute_New(udt_Connection*, OCIParam*); static void ObjectAttribute_Free(udt_ObjectAttribute*); static PyObject *ObjectAttribute_Repr(udt_ObjectAttribute*); @@ -45,7 +45,8 @@ static PyObject *ObjectAttribute_Repr(udt_ObjectAttribute*); // declaration of methods for Python type "ObjectType" //----------------------------------------------------------------------------- static PyMethodDef g_ObjectTypeMethods[] = { - { "newobject", (PyCFunction) ObjectType_NewObject, METH_NOARGS }, + { "newobject", (PyCFunction) ObjectType_NewObject, + METH_VARARGS | METH_KEYWORDS }, { NULL, NULL } }; @@ -92,7 +93,7 @@ static PyTypeObject g_ObjectTypeType = { 0, // tp_as_sequence 0, // tp_as_mapping 0, // tp_hash - 0, // tp_call + (ternaryfunc) ObjectType_NewObject, // tp_call 0, // tp_str 0, // tp_getattro 0, // tp_setattro @@ -529,32 +530,36 @@ static PyObject *ObjectType_Repr( //----------------------------------------------------------------------------- static PyObject *ObjectType_NewObject( udt_ObjectType *self, // object type to return the string for - PyObject *args) // arguments (none, ignored) + PyObject *args, // arguments + PyObject *keywordArgs) // keyword arguments { - dvoid *instance, *indicator; - sword status; + static char *keywordList[] = { "value", NULL }; + PyObject *initialValue; + udt_Object *obj; - // create the object instance - status = OCIObjectNew(self->connection->environment->handle, - self->connection->environment->errorHandle, - self->connection->handle, self->typeCode, self->tdo, NULL, - OCI_DURATION_SESSION, TRUE, &instance); - if (Environment_CheckForError(self->connection->environment, status, - "ObjectType_NewObject(): create object instance") < 0) + // parse arguments + initialValue = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|O", keywordList, + &initialValue)) 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) + // create the object + obj = Object_Create(self); + if (!obj) return NULL; - return Object_New(self, instance, indicator, 1); + // populate collection, if applicable + if (initialValue) { + if (Object_InternalExtend(obj, initialValue) < 0) { + Py_DECREF(obj); + return NULL; + } + } + + return (PyObject*) obj; } -static PyObject *ObjectType_NewObject(udt_ObjectType*, PyObject*); //----------------------------------------------------------------------------- // ObjectAttribute_Initialize() // Initialize the new object attribute. diff --git a/doc/objecttype.rst b/doc/objecttype.rst index cc23119..0b2d560 100644 --- a/doc/objecttype.rst +++ b/doc/objecttype.rst @@ -11,6 +11,12 @@ Object Type Objects :data:`~Variable.type` for variables containing Oracle objects. +.. method:: ObjectType([sequence]) + + The object type may be called directly and serves as an alternative way of + calling newobject(). + + .. attribute:: ObjectType.attributes This read-only attribute returns a list of the attributes that make up the @@ -28,11 +34,13 @@ Object Type Objects This read-only attribute returns the name of the type. -.. method:: ObjectType.newobject() +.. method:: ObjectType.newobject([sequence]) 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. + interaction with Oracle. If the object type refers to a collection, a + sequence may be passed and the collection will be initialized with the + items in that sequnce. .. attribute:: ObjectType.schema diff --git a/test/ObjectVar.py b/test/ObjectVar.py index 80bceb3..d251fbb 100644 --- a/test/ObjectVar.py +++ b/test/ObjectVar.py @@ -75,6 +75,20 @@ class TestObjectVar(BaseTestCase): "udt_Object(null, 'Test With Dates', null, null, null, " \ "udt_SubObject(15, 'Sub String'), null)") + def testCopyObject(self): + "test copying an object" + typeObj = self.connection.gettype("UDT_OBJECT") + obj = typeObj() + obj.NUMBERVALUE = 5124 + obj.STRINGVALUE = "A test string" + obj.DATEVALUE = datetime.datetime(2016, 2, 24) + obj.TIMESTAMPVALUE = datetime.datetime(2016, 2, 24, 13, 39, 10) + copiedObj = obj.copy() + self.assertEqual(obj.NUMBERVALUE, copiedObj.NUMBERVALUE) + self.assertEqual(obj.STRINGVALUE, copiedObj.STRINGVALUE) + self.assertEqual(obj.DATEVALUE, copiedObj.DATEVALUE) + self.assertEqual(obj.TIMESTAMPVALUE, copiedObj.TIMESTAMPVALUE) + def testFetchData(self): "test fetching objects" self.cursor.execute("""